赞
踩

【解题】
按题意模拟即可,先按分钟变化,当体力不足600时跳出循环,按比例再加上相应的秒数即可。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int main() { int i,j; int ti=0; int power=10000; while(1) { if(power>=600) { power-=600; ti+=60; } else break; power+=300; ti+=60; } ti+=power/10;//每分钟减600,均匀减小,故每秒减600/60=10; printf("%d\n",ti); return 0; } //ans==3880;
ans:3880

【解题】
[更新解法] 由于该国民众的被感染率为1%,并且为均匀分布,那么相当于100个人里面有1个感染,因此当k>=100的值的时候,合并检测中必定有阳性,会导致所有人都要单独检测,因此k的遍历返回可以缩小至 [1,100) 区间中。
取100个人,则有1人是被感染的。
假设分成k组,则每组有[(100/k)向上取整]个人。
则所需试剂盒数量为:
f(k)=k+[100/k](其中[]表示向上取整)
其中前半部分是k组合并检测需要k个试剂盒;后半项是被感染者所在的组所有人要再单独检测。
遍历k∈[1,100)即可。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int main(){ int k; int minn=101;//最少的试剂盒数量; int minpos=0;//取最小值时的k的值; //f(k)=k+[100/k] for(k=1;k<100;k++){ int temp; if(100%k==0) temp=k+100/k; else temp=k+100/k+1; if(temp<minn){ minn=temp; minpos=k; } } printf("%d\n",minpos); return 0; } //ans:10
ans:10

【解题】
这个题就是把数组分成两个部分,求两部分和最小值的包装版本,可以用暴力dfs也可以dp(0-1背包作背包容量为sum/2的dp),结果填空且只有15个数,直接暴力搜即可。
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; typedef long long ll; ll num[15]={9090400,8499400,5926800,8547000,4958200,4422600,5751200,4175600,6309600,5865200,6604400,4635000,10663400,8087200,4554000}; ll minn=0x3f3f3f3f; ll sum; void dfs(int pos,ll now) { if(pos>=15) { minn=min(minn,abs(sum-2*now)); return ; } dfs(pos+1,now+num[pos]);//选 dfs(pos+1,now); //不选 } int main() { int i,j; int n,m; sum=0; for(i=0;i<15;i++) { sum+=num[i]; } dfs(0,0); printf("%lld\n",minn); return 0; } //ans==2400;
ans:2400

【解题】
DP进行方案数计数,dp(i,j)表示前i个数选其中j个放入第一行,转移策略如代码中注释所示。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=2500; int dp[maxn][maxn];//dp[i][j]表示前i个数放其中j个到第一行的方案数; int main() { int i,j; memset(dp,0,sizeof(dp)); dp[1][1]=1;//1肯定放到第一行(若放到第二行不可能比第一行大) //维护dp表的下三角即可(j>i的上三角无意义) //dp策略: //1.数字放在第一行肯定可以,后面遍历的数字越来越大; //2.数字放在第二行的条件是目前第一行的数字比第二行多, // 因为若目前少,则之后肯定得有更大的数放到第一行,与要求冲突 for(i=2;i<=2020;i++) { for(j=1;j<=i;j++) { dp[i][j]=(dp[i][j]+dp[i-1][j-1])%2020;//放到第一行; if(i-j<=j)//i-j为放第二行的数字数,即放第二行的数字数小于第一行的数字数的情况(此时i是正要遍历的,所以等号可取) { //放到第二行; dp[i][j]=(dp[i][j]+dp[i-1][j])%2020; } } } printf("%d\n",dp[2020][1010]); return 0; } //ans==1340
ans:1340

【解题】
感谢评论区大佬提供的思路,打表发现所述完美平方数必须为各数位值均属于{0,1,4,9}组成的数。
因此可以用0,1,4,9在各数位枚举去构造,这样时间复杂度是4^len(其中len是数字的长度),但是这样还是无法在有限时间内找到第2020个数,只能找到大概第900个数,后面速度就比较慢了,因为第2020个数估计出现在len=19的位置,因此需要4 ^ 19的时间…
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; typedef long long ll; int base[]={0,1,4,9}; ll ans; int tot=0; bool fin=false; //用{0,1,4,9}为各数位值去构造. void dfs(int pos,int len,ll sum){ if(pos>len){ if((ll)(sqrt(sum))*(ll)(sqrt(sum))==sum){ tot++; printf("%d %lld\n",tot,sum); } if(tot==2020){ ans=sum; fin=true; } return ; } int start; if(pos==1) start=1;//最高位不能为0,否则会重复计数. else start=0; for(int i=start;i<4&&!fin;i++){ dfs(pos+1,len,sum*10+base[i]); } return ; } int main() { ll i; tot=4;//单个位数的先初始出来(0,1,4,9); ll ans; int len=2; while(tot<2020) { dfs(1,len,0); len++; } printf("%lld\n",ans); return 0; } //ans:
打表程序:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; //打表程序 int main() { ll i; int tot=7;//现在已找到第7个(0,1,4,9,49,100,144); i=13;//12*12==144,从13*13开始即可 while(tot<50) { ll num=i*i; while(num!=0) { ll now=num%10; if(now!=0&&now!=1&&now!=4&&now!=9)//数位不是完全平方数 { break; } num/=10; } if(num==0) { tot++; printf("%lld\n",i*i); } i++; } return 0; }

【解题】
这题也是思路很直接,若是数字再补上num-1个字母即可。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=105; char s1[maxn]; char s2[maxn*maxn]; int main() { int i,j; scanf("%s",s1); char now=s1[0]; s2[0]=now; int len=0; for(i=1;i<strlen(s1);i++) { if(s1[i]>='0'&&s1[i]<='9') { for(j=1;j<s1[i]-'0';j++) { s2[++len]=now; } } else { now=s1[i]; s2[++len]=now; } } s2[++len]='\0'; printf("%s\n",s2); return 0; }


【解题】
经典DP计数题,加了一个行号列号偶数不能走的限制。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=50; int dp[maxn][maxn]; int main() { int i,j; int n,m; scanf("%d%d",&n,&m); if(n%2==0&&m%2==0) printf("0\n"); else { memset(dp,0,sizeof(dp)); dp[1][1]=1; for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { if(i%2==0&&j%2==0) dp[i][j]=0; else { dp[i][j]=max(dp[i][j],dp[i-1][j]+dp[i][j-1]); } } } printf("%d\n",dp[n][m]); } return 0; }

【解题】
将各数字以字符串形式输入,按长度排序,对每次遍历得到的两个数字,若两数字的长度之和小于K的长度,直接ans+2,若两数的长度之和大于K的长度,直接break出遍历(因为按长度排序了,现在的长度大于了K,之后的长度肯定也大于K),这样其实是O(n^2)+一点优化(对于n为10 ^ 5的数据量其实是可以卡的,此处有没有更优的方法?)
#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; const int maxn=1e5+6; typedef long long ll; struct node{ char s[20]; int len; }a[maxn]; char str[20]; bool cmp(node a,node b) { return a.len<b.len; } ll get_value(char* s) { ll value=0; for(int i=0;i<strlen(s);i++) { value=value*10+s[i]-'0'; } return value; } int main() { int i,j,k; int n; /* 以字符串形式输入: 若len<lenk,直接选; 若len==lenk再判断; 若len>lenk直接出来; */ scanf("%d %s",&n,str); int l=strlen(str); for(i=1;i<=n;i++) { scanf("%s",&a[i].s); a[i].len=strlen(a[i].s); //printf("%s",a[i].s); } ll ans=0; ll str_v=get_value(str); //printf("%lld\n",str_v); sort(a+1,a+1+n,cmp); for(i=1;i<=n;i++) { bool fin=false; for(j=i+1;j<=n;j++) { if(a[i].len+a[j].len<l) { ans+=2; } else if(a[i].len+a[j].len==l) { ll v1=get_value(a[i].s); ll v2=get_value(a[j].s); ll va=v1*pow(10,a[j].len)+v2; ll vb=v2*pow(10,a[i].len)+v1; if(va<=str_v) ans++; if(vb<=str_v) ans++; } else { fin=true; break;//因为按长度从小到大排,现在不行之后的肯定也不行; } } if(fin) break; } printf("%lld\n",ans); return 0; }


【解题】
这道题第一眼是贪心,第二眼是区间DP,但瞄了一下数据范围,贪心或区间DP应该都不能拿满(估计O(n^2)),手算了一波好像按照花费是乘积,重量变成两者之和这种约束是无论怎么选总花费都一样的…都为各重量两两乘积的和。
数学归纳法证明:
设总花费为f 当n=2时,设石头重量分别为a,b,则f=a*b正确; 当n=3时,设石头重量分别为a,b,c, 则所有合并方式分别为: f1=a*b+(a+b)*c=a*b+a*c+b*c; f2=a*c+(a+c)*b=a*b+a*c+b*c; f3=b*c+(b+c)*a=a*b+a*c+b*c; f=f1=f2=f3,正确; 假设当n=k时结论正确,设石头重量分别为a1,a2,...ak 即f=a1*a2+a1*a3+...+a1*an+...+a(k-1)*ak 当n=k+1时,设石头重量分别为a1,a2,...ak,a(k+1) 无论将a(k+1)在哪一步进行合并, 其都可以看成是a(k+1)与ai的一个置换(1<=i<=k) 设a(k+1)与其中的ai进行了置换, 石头表示为(a1,a2,...,a(k+1),...ak),ai 对于前面括号中的部分,由假设的n=k的情况可以得到: f1=a1*a2+...a1*an+...a(k+1)*a(i+1)+...+a(k+1)*ak+...+a(k-1)*ak 对前面括号的合并后可以看成剩两堆石头X,ai, 其中X的重量为a1+a2+...+a(k+1)+...+ak 对数量为2的情况, 花费f2=(a1+a2+...+a(k+1)+...+ak)*ai =a1*ai+a2*ai+...ak*ai+a(k+1)*ai 故当n=k+1时, 总花费为f=f1+f2=a1*a2+...+a1*a(k+1)+...+ak*a(k+1) 因此假设正确。
故得到各重量两两乘积的和即可,时间复杂度O(n)
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=1e5+6; typedef long long ll; ll num[maxn]; ll sum,ans; int main() { int i,j; int n; scanf("%d",&n); for(i=1;i<=n;i++) { scanf("%d",&num[i]); } sum=num[1]; ans=0; for(i=2;i<=n;i++) { ans+=sum*num[i]; sum+=num[i];//前i个石头的重量和; } printf("%lld\n",ans); return 0; }


【解题】
感谢评论区大佬提供的思路,采用类似带权并查集的思想可以在O(m*log(m))的时间完成(朴素的并查集+遍历更新需要O(n * m)的时间复杂度)。
思路为:相较于朴素并查集,增加一个value数组存储当前节点到其父节点的边权,这里边权value[i]的含义是节点i的当前存储信息量,借助find函数路径压缩的时候同时更新。每次在某节点发送信息时,方法是找到根节点,建立一个虚节点作为根节点的父节点,该虚节点成为新的根节点。
(其实就是借助树的层次来区别不同节点所累加的信息量,越靠近根节点的节点是越晚加入的,可以画图理解)
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=2e5+5; int f[maxn],value[maxn]; //其中,f[i]表示的是节点i的父节点id,value[i]记录节点i与其父节点之间的边权. int find(int x){ if(x==f[x]) return x; else{ int z=f[x];//记录路径压缩前原父节点的id f[x]=find(f[x]); value[x]+=value[z]; return f[x]; } } int main(){ int i,j; int n,m; scanf("%d%d",&n,&m); for(i=1;i<=n;i++){ f[i]=i; value[i]=0; } int op,a,b; int tot=n;//当前总节点数,每次发送测试信息时,建立虚节点作为新的根节点 for(i=1;i<=m;i++){ scanf("%d%d%d",&op,&a,&b); //op==1表示连通节点a和节点b; //op==2表示在节点p发送一条大小为t的信息; if(op==1){ int fa=find(a); int fb=find(b); if(fa!=fb){ f[fa]=fb; value[fa]=0; //连通后两节点还没有发送信息,因此边权初始化为0. } } else{ int fp=find(a); tot++;//建立虚节点 value[tot]=0; f[tot]=tot; f[fp]=tot; value[fp]=b; } } for(i=1;i<=tot;i++){ int temp=find(i);//最后再更新一次,将根节点权值更新到各个节点. } for(i=1;i<=n;i++){ if(i!=n) printf("%d ",value[i]); else printf("%d\n",value[i]); } //printf("%d\n",ans); return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。