1 条题解

  • 3
    @ 2025-10-3 14:11:44

    这么难的题居然只是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;
    }
    

    可能有点凌乱,各位将就这看吧。

    信息

    ID
    724
    时间
    1000ms
    内存
    256MiB
    难度
    9
    标签
    递交数
    129
    已通过
    14
    上传者