1 条题解
-
3
这么难的题居然只是9级吗?(我是飞舞
首先对于有趣数,我是手算了十几个有趣数,当然你用代码跑出来也是可以的。
这一题的核心在于,序列n的各项和是不变的,明确这一点之后,注意到题目特意限制了可操作数的最小值为2,意味着定义上合理的有趣数0,在代码中是不能被采用的,也就是说,最终调完的n序列,里面有趣数的最小值为1.
接下来看思路,(具体可以看代码注释,我会好好写的)我的思路是:先获得序列n的总和,找出原本已经是有趣数,无需调整的数,剔除,接下来通过查表(下述),判断是否能全部是有趣数,如果不行,抛掉一个数,剩下的数是否大于剩下数的个数(如第一次循环,剩下n-1个数,判断这n-1个数的和是否大于n-1)(因为有趣数最小值为1)如果大于,此时的数就能全部变成有趣数,如果不行,继续向下查找。
表的获取:先在循环T之外,跑出一个bool类型的二维数组(防超时),这个二维数组存储着对应k个有趣数,它的和可能是多少。用到完全背包的思想,(我头铁完才知道)每加入一个新有趣数 x,就更新所有 k 和 s:如果 “用 k-1 个有趣数能凑出 s-x”(即 dp [k-1][s-x] 为 true),那么 “用 k 个有趣数就能凑出 s”(即 dp [k][s] 记为 true)。
下面是代码
#include<stdio.h> #include<string.h> #include<stdlib.h> #include<math.h> #include<stdbool.h> bool dp[101][20001]={false}; //上述二维数组 bool shi(int x) { int m=sqrt(x); if(m*m!= x) return false; int sum_d=0; while(x>0){ sum_d+=x%10; x/=10; } int k=sqrt(sum_d); return k*k==sum_d; }//判断这个数是不是有趣数,用于后续判断 int main(){ dp[0][0]=true;//初始化 int youqu[]={1,4,9,36,81,100,121,144,169,196,225,324,400,441,484,529}; int i,j,k; int len=sizeof(youqu)/sizeof(youqu[0]); for(i=0;i<len;i++){ int yq=youqu[i]; for(j=1;j<=100;j++){ for(k=yq;k<=20000;k++){ if(dp[j-1][k-yq]){ dp[j][k]=true; //核心逻辑 } } } } int T;scanf("%d",&T); for(i=0;i<T;i++){ int n;scanf("%d",&n); int a[n]; int suma=0; bool kecao=false;//用于判断一个数是否可以操作 int yuanlai=0;//原来数组里有趣数的个数 for(j=0;j<n;j++){ scanf("%d",&a[j]); suma+=a[j];//获取序列总和 if(a[j]>=2) kecao=true;//只需要一个数就能完成操作 if(shi(a[j])) yuanlai++; } int ans=0;//暂时存储结果 for(k=n;k>=0;k--){ if(k==0){ ans=0; break; //空 } if((k==n&&suma<=20000&&dp[k][suma])||(k<n&&(kecao||suma>=k))){ //(k==n全部数 &&suma<=20000没有超过 &&dp[k][suma]查表判断是否存在) //k<n部分数字 &&(kecao||suma>=k)可以操作或者总和大于等于k //说明:可以操作(kecao=true)就是能够全部变成有趣数,因为序列n的所有值不小于1 //总和大于k,必然可以通过加一减一的方式获得全1的有趣数 ans=k; break; } } printf("%d\n",ans); } return 0; }
可能有点凌乱,各位将就这看吧。
- 1
信息
- ID
- 724
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 9
- 标签
- 递交数
- 129
- 已通过
- 14
- 上传者