P1618 三连击(升级版)
原题地址:https://www.luogu.com.cn/problem/P1618
题目描述:将 1, 2,9 共 9 个数分成三组,分别组成三个三位数,且使这三个三位数的比例是 A:B:C,试求出所有满足条件的三个三位数,若无解,输出 No!!!
。
输入格式:三个数,A,B,C。
输出格式:若干行,每行 3 个数字。按照每行第一个数字升序排列。
一开始的思路是打表从一个存储123456789的数组里直接选数,保证选完的不再选(可以用一个bool数组存放“是否已选”),但我不会搜索、动态规划什么的,逻辑能力十分弱,这种解法真的想不出来。
这里要感谢大佬 可耐滴小慕容 (主页->)https://www.luogu.com.cn/user/24663#main的题解,ta启发了我的新思路(我真的没抄袭复制!所有代码都是我自己写的!只是ta的思路启发了我!这应该不算抄袭吧!)
可以直接枚举第一个数,根据比例算出来另外两个数,再进行判断!(这句话是大佬的知识产权)
首先设计出判断数字位数和获取数字某一位的数字的函数,以备之后使用:
int getCertainNumOfInt(int a,int num)//返回数字串a的第num位数(从右往左数)。 { int ten=1,q; for(q=1;q<num;q++) { ten*=10; } int r=a/ten%10; return r; } int get_length(int a)//返回数字a的位数。 { int leng=0; while(a) { a/=10; leng++; } return leng; }
然后是存储1—9数字是否用过的数组:
int p[10]={0,0,0,0,0,0,0,0,0,0};
(p[0]纯属占位)
然后,判断一个三位数本身是否重复的函数(自己就重复就肯定不对了)
bool isNoRepeat(int a) { int ge=a%10;//个位数 int bai=a/100;//百位数 int shi=(a/10)%10;//十位数 if(ge==bai||ge==shi||bai==shi) return false;//只要有相同的数就不满足条件 else return true; }
接着是判断三个三位数的数字是否互不重复:
bool isAllNoRepeat(int a,int b,int c) { if(!(isNoRepeat(a)&&isNoRepeat(b)&&isNoRepeat(c))) return false;//只要自身重复了就不对 else { for(int i=1;i<=3;i++) { p[getCertainNumOfInt(a,i)]++; } for(int i=1;i<=3;i++) { p[getCertainNumOfInt(b,i)]++; } for(int i=1;i<=3;i++) { p[getCertainNumOfInt(c,i)]++; } } for(int i=1;i<10;i++) { if(p[i]!=1)//有0代表有的数字没选,有大于一的数字代表有的数字选了多次,都不对 { memset(p,0,sizeof(p));//别忘了原数组清零,供下一次使用 return false; } } memset(p,0,sizeof(p));//别忘了原数组清零,供下一次使用 return true; }
以下是main的第一版代码(不对的一版):
int main() { int a,b,c; cin>>a>>b>>c; int x,y,z;//存放根据比例算出的三个三位数 int tot=0; for(int i=100;i<=999;i++) { if(!isNoRepeat(i)) continue;//自己要不重复 x=i; y=x/a*b;//潜在问题 z=x/a*c;//潜在问题 if(x<1000&&y<1000&&z<1000/*必须是三位数*/&&isAllNoRepeat(x,y,z)) { cout<<x<<" "<<y<<" "<<z<<endl; tot++; } } if(!tot) cout<<"No!!!";//没有答案 return 0; }
注意到潜在问题了吗?可能有极特殊的情况,由于xyz的精度不高,可能算不出精确的三位数!最后,这种代码导致了一个监测点WA。
第二版代码:
int main() { int a,b,c; cin>>a>>b>>c; double x,y,z; int tot=0; for(int i=100;i<=999;i++) { if(!isNoRepeat(i)) continue; x=(double)i; y=x/(double)a*b; z=x/(double)a*c; if(x<1000&&y<1000&&z<1000&&isAllNoRepeat(x,y,z)) { cout<<x<<" "<<y<<" "<<z<<endl; tot++; } } if(!tot) cout<<"No!!!"; return 0; }
double换取了更高的精度,算出的数字排除了特殊情况。全部AC!
这个题让我学到了:1.换一种思路!方法总比困难多!
2.用除法的时候要特别考虑精度(即用int还是double)