第七届蓝桥杯C/C++程序设计本科B组决赛 ——凑平方数(填空题)


凑平方数

把0~9这10个数字,分成多个组,每个组恰好是一个平方数,这是能够办到的。
比如:0, 36, 5948721

再比如:
1098524736
1, 25, 6390784
0, 4, 289, 15376
等等...

注意,0可以作为独立的数字,但不能作为多位数字的开始。
分组时,必须用完所有的数字,不能重复,不能遗漏。

如果不计较小组内数据的先后顺序,请问有多少种不同的分组方案?

注意:需要提交的是一个整数,不要填写多余内容。


错误题解(答案错误原因未知、暴力五重for循环——超时,优化再优化超时!)——可供借鉴!

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <iostream>
  4 #include <algorithm>
  5 #include <queue>
  6 #include <stack>
  7 #include <vector>
  8 #include<math.h>
  9 #include <string.h>
 10 #include<set>
 11 using namespace std;
 12 #define inf 0x3f3f3f3f
 13 #define maxn 10000000
 14 const double pi=acos(-1.0);
 15 #define ll long long
 16 #define N 100008
 17 using namespace std;
 18 vector<ll>a;
 19 int getlen(ll x){// 返回x的数字位数
 20     return (int)log10(x*1.0)+1;
 21 }
 22 int cmp1(ll x){   //cmp1()表示传入的1个数是否没有重复数码
 23     int arr[10]={0};
 24     while(x>0){
 25         arr[x%10]++;x/=10;
 26     }
 27     for(int i=0;i<=9;i++)
 28         if(arr[i]>1)return 0;
 29     return 1;
 30 }
 31 int cmp2(ll x,ll y){ //cmp2--5()表示传入的2--5个数是否可以不重不漏地构成十位数码
 32     if(getlen(x)+getlen(y)>10)
 33         return 3;
 34     int arr[10]={0};
 35     while(x>0){
 36         arr[x%10]++;x/=10;
 37     }
 38     while(y>0){
 39         arr[y%10]++;y/=10;
 40     }
 41     for(int i=0;i<=9;i++){
 42        if(arr[i]>1)return 0;
 43        else if(arr[i]==0)return 2;//缺省时
 44     }
 45 
 46     return 1;
 47 
 48 }
 49 int cmp3(ll x,ll y,ll z){
 50     if(getlen(x)+getlen(y)+getlen(z)>10)
 51         return 3;
 52     int arr[10]={0};
 53     while(x>0){
 54         arr[x%10]++;x/=10;
 55     }
 56     while(y>0){
 57         arr[y%10]++;y/=10;
 58     }
 59      while(z>0){
 60         arr[z%10]++;z/=10;
 61     }
 62    for(int i=0;i<=9;i++){
 63        if(arr[i]>1)return 0;
 64        else if(arr[i]==0)return 2;//缺省时
 65     }
 66     return 1;
 67 }
 68 int cmp4(ll x,ll y,ll z,ll h){
 69     if(getlen(x)+getlen(y)+getlen(z)+getlen(h)>10)
 70         return 3;
 71     int arr[10]={0};
 72     while(x>0){
 73         arr[x%10]++;x/=10;
 74     }
 75     while(y>0){
 76         arr[y%10]++;y/=10;
 77     }
 78     while(z>0){
 79         arr[z%10]++;z/=10;
 80     }
 81     while(h>0){
 82         arr[h%10]++;h/=10;
 83     }
 84     for(int i=0;i<=9;i++){
 85        if(arr[i]>1)return 0;
 86        else if(arr[i]==0)return 2;//缺省时
 87     }
 88     return 1;
 89 
 90 }
 91 int cmp5(ll x,ll y,ll z,ll h,ll t){//表示传入的五个数是否可以不重不漏地构成十位数码
 92     if(getlen(x)+getlen(y)+getlen(z)+getlen(h)+getlen(t)>10)
 93         return 3;
 94     int arr[10]={0};
 95     while(x>0){
 96         arr[x%10]++;x/=10;
 97     }
 98     while(y>0){
 99         arr[y%10]++;y/=10;
100     }
101     while(z>0){
102         arr[z%10]++;z/=10;
103     }
104     while(h>0){
105         arr[h%10]++;h/=10;
106     }
107     while(t>0){
108         arr[t%10]++;t/=10;
109     }
110     for(int i=0;i<=9;i++){
111        if(arr[i]>1)return 0;
112        else if(arr[i]==0)return 2;//缺省时
113     }
114     return 1;
115 
116 }
117 
118 int main(){
119 
120     ll maxx=9876543210;//确定上限
121     int cnt=0;
122     a.clear();
123     for(ll i=0;i<=100000;i++){//筛选出制定范围内的所有平方数
124         if(i*i>maxx)break;
125         if(cmp1(i*i)==1)////将自身数码不重复的平方数存入a中
126             a.push_back(i*i);
127     }
128 
129     cnt=a.size();
130     printf("总的平方数==%d %lld
",cnt,a[cnt-1]);//611 9814072356
131 
132     int ans=0;
133     for(int i=cnt-1;i>=0;i--){///把后面的十位数平方数全部删除掉
134         if(getlen(a[i])==10){
135             ans++;a.pop_back();
136         }
137         else
138             break;
139     }
140     cnt=a.size();
141     printf("10位的平方数有ans=%d %d %lld
",ans,cnt,a[cnt-1]);//87,524
142     /*由于接下来只剩1--9位的平方数了,可以进行两两枚举、三三枚举、四四五五地枚举;
143     原先就是如下写的————
144     for(int i1=0;i1<cnt;i1++){
145         for(int i2=i1+1;i2<cnt;i2++){
146             if(cmp2(a[i1],a[i2])==1)
147                 ans++;
148         }
149     }
150     for(int i1=0;i1<cnt;i1++){
151         for(int i2=i1+1;i2<cnt;i2++){
152             for(int i3=i2+1;i3<cnt;i3++){
153                 if(cmp3(a[i1],a[i2],a[i3])==1)
154                     ans++;
155             }
156         }
157     }
158     for(int i1=0;i1<cnt;i1++){
159         for(int i2=i1+1;i2<cnt;i2++){
160             for(int i3=i2+1;i3<cnt;i3++){
161                 for(int i4=i3+1;i4<cnt;i4++){
162                     if(cmp4(a[i1],a[i2],a[i3],a[i4])==1)
163                         ans++;
164                 }
165             }
166         }
167     }
168     for(int i1=0;i1<cnt;i1++){
169         for(int i2=i1+1;i2<cnt;i2++){
170             for(int i3=i2+1;i3<cnt;i3++){
171                 for(int i4=i3+1;i4<cnt;i4++){
172                      for(int i5=i4+1;i5<cnt;i5++){
173                         if(cmp5(a[i1],a[i2],a[i3],a[i4],a[i5])==1)
174                             ans++;
175                     }
176                 }
177             }
178         }
179     }
180 
181     */
182     //后来发现这其中有大量重复计算,在上面两两枚举中,若一对数有冲突仍会进行往下跑循环,
183     //再细想,发现三重循环实际上包含了两重循环,四重含三重和两重,五重......
184     //所以可以进行简单优化,continue,并且有了下面的四合一程序
185     int k;
186     printf("ans有:ans=%d
",ans);
187     ll Time=0;
188     for(int i1=0;i1<cnt;i1++){
189         for(int i2=i1+1;i2<cnt;i2++){
190                 if(k=cmp2(a[i1],a[i2]),k==1)
191                     ans++;
192                 if(k==0||k==1)//k=0有重复或者k=1找到答案,k==2表示缺省(可以向下搜),k==3表示总位数超10——break
193                     continue;
194                 if(k==3)break;
195             for(int i3=i2+1;i3<cnt;i3++){
196                     if(k=cmp3(a[i1],a[i2],a[i3]),k==1)
197                         ans++;
198                     if(k==0||k==1)//有重复或者已经全部使用过了
199                         continue;
200                     if(k==3)break;
201                 for(int i4=i3+1;i4<cnt;i4++){
202                         if(k=cmp4(a[i1],a[i2],a[i3],a[i4]),k==1)
203                             ans++;
204                         if(k==0||k==1)//有重复或者已经全部使用过了
205                             continue;
206                         if(k==3)break;
207                     for(int i5=i4+1;i5<cnt;i5++){
208                         if(k=cmp5(a[i1],a[i2],a[i3],a[i4],a[i5]),k==1)
209                             ans++;
210                          if(k==3)break;
211                         Time++;
212                         if(Time%(ll)100000==0)printf("%3d,%3d,%3d,%3d,%3d, Time=%5lld十万
",i1,i2,i3,i4,i5,Time/(ll)100000);
213                     }
214                 }
215             }
216         }
217     }
218 
219     printf("总ans有:ans=%d
",ans);
220     return 0;
221 }
View Code

计算一下时间复杂度,很重要!如果很大很大,就建议直接放弃,换种思路!


正确题解(一维搜索,可以有效降低时间复杂度,因为在一维数组a[]中,元素是递增的--元素的长度就是非递减的,一旦某一个因为长度超出后不合适——后续的都不合适了)。

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include<math.h>
#include <string.h>
#include<set>
using namespace std;
#define inf 0x3f3f3f3f
#define maxn 10000000
const double pi=acos(-1.0);
#define ll long long
#define N 10008
using namespace std;
ll a[N];

int check(string num){//判断字符串num是否重复
    int arr[10]={0};
    int len=num.length();
    for(int i=0;i<len;i++){
        int t=num[i]-'0';
        arr[t]++;
        if(arr[t]>1)return 0;
    }
    return 1;
}
string turn(ll num){//将num转化成字符串进行输出
    string s;
    if(num==0)s+="0";
    while(num>0){
        char ch[]={num%10+'0',''};//临时字符串,用于string的插入
        s.insert(0,ch);
        num/=10;
    }
    return s;
}
int ans;//在a[]数组中进行一维搜索,每次选取step的位置,然后再往后选取一位合适的
void dfs(int step,string s,int cnt){

    if(s.length()>10||check(s)==0)return ;

    if(s.length()==10&&check(s)==1){
        ans++;
        return ;
    }
    for(int i=step;i<=cnt;i++){
        dfs(i+1,s+turn(a[i]),cnt);
    }

}
int main(){

    ll maxx=9876543210;
    int cnt=0;
    for(ll i=0;i<=100000;i++){//生成完全平方数表,共cnt个
        if(i*i<=maxx){
            if(check(turn(i*i)))
                    a[++cnt]=i*i;
        }
        else break;
    }
    printf("完全平方数cnt=%d
",cnt);
    ans=0;
    dfs(1,"",cnt);
    printf("%d
",ans);

    return 0;
}
View Code

【正确答案:300】

原文地址:https://www.cnblogs.com/zhazhaacmer/p/9041390.html