算法基础-1-枚举
1.枚举
1.1 完美立方
形如a3>= b3 + c3+ d3的等式被称为完美立方等式。例如123= 63 + 83+ 103。编写一个程序,对任给的正整数N(N≤100),寻找所有的四元组(a,b,c,d),使得a3=b3 + c3+ d3,其中a,b,c,d 大于1,小于等于N,且b<=c<=d。
输入
一个正整数N (N≤100)。
输出
每行输出一个完美立方。输出格式为:
Cube = a,Triple = (b,c, d),
其中a,b,c,d所在位置分别用实际求出四元组值代入。
请按照a的值,从小到大依次输出。当两个完美立方等式中a的值相同,则b值小的优先输出、仍相同则c值小的优先输出、再相同则d值小的先输出。
解题思路:
四重循环枚举a,b,c,d,a在最外层,d在最里层,每一层都是从小到大枚举
a枚举范围[2,N]
b范围[2, a-1]
#include <iostream> #include <cstdio> using namespace std; int main() { int N; scanf("%d", &N); for (int a = 2; a <= N; ++a) for (int b = 2; b < a; ++b) for (int c = b; c < a; ++c) for (int d = c; d < a; ++d) if (a * a * a == b * b * b + c * c * c + d * d * d) printf("Cube = %d, Triple = (%d,%d,%d)\n",a,b,c,d); return 0; }
1.2 生理周期
人有体力、情商、智商的高峰日子,它们分别每隔23天、28天和33天出现一次。对于每个人,我们想知道何时三个高峰落在同一天。给定三个高峰出现的日子p,e和i (不一定是第一次高峰出现的日子),再给定另一个指定的日子d,你的任务是输出日子d之后,下一次三个高峰落在同一天的日子(用距离d的天数表示)。例如:给定日子为10,下次出现三个高峰同一天的日子是12,则输出2。
输入
四个整数: p,e,i和d。p, e,i分别表示体力、情感和
智力高峰出现的日子。d是给定的日子,可能小于p,e或i。所有给定日子是非负的并且小于或等于365,所求的日子小于或等于21252。
输出
从给定日子起,下一次三个高峰同一天的日子的天数)。
- 解题思路
从d+1天开始,一直试到第21252天,对其中每个日期k,看是否满足(k-p)%23 ==0&&(k-e)%28 ==0 &&(k-i)%33== 0
如何试得更快?
找到一个周期后,寻找下一个周期每次枚举增加的天数为已找到的周期(的最小公倍数)。
#include <iostream> #include <cstdio> using namespace std; #define N 21252 int main() { int p, e, i, d, caseNo = 0; while (cin >> p >> e >> i >> d && p != -1) { ++caseNo; int k; for (k = d + 1; (k - p) % 23; ++k); for (; (k - e) % 28; k += 23); for (; (k - i) % 33; k += 23 * 28); cout << "Case "<< caseNo <<" : the next triple peak occurs in "<< k-d <<""; } return 0; }
1.3 称硬币
有12枚硬币。其中有11枚真币和1枚假币。假币和真币重量不同,但不知道假币比真币轻还是重。现在,用一架天平称了这些币三次,告诉你称的结果,请你找出假币并且确定假币是轻是重(数据保证一定能找出来)。
输入
1 注意:天平左右的硬币数总是相等的,这里的up/down指右边翘起/下沉
ABCD EFGH even
ABCI EFJK up
ABIJ EFGH even
输出
K is the counterfeit coin and it is light.
解题思路
对于每一枚硬币先假设它是轻的,看这样是否符合,称量结果。如果符合,问题即解决。如果不符合,就假设它是重的,看是否符合称量结果。把所有硬币都试一遍,一定能找到特殊硬币。如何判断一个假设结果为真?
#include <iostream> #include <cstring> using namespace std; char Left[3][7]; //天 平左边硬币 char Right[3][7]; //天 平右边硬币 char result[3][7]; //结果 bool IsFake(char c, bool light); //light为真表示假设假币为轻,否则表示假设假币为重 int main() { int t; cin >> t; while (t--) { for (int i = 0; i < 3; ++i) cin >> Left[i] >> Right[i] >> result[i]; for (char C = 'A'; C <= 'L'; C++) { if (IsFake(C, true)) { cout << C << " is the counterfeit coin and it is light.\n"; break; } else if (IsFake(C, false)) { cout << C < < < " is the counterfeit coin and it is heavy. \n"; break; } } } } bool IsFake(char c, bool light) //light为真表示假设假币为轻,否则表示假设假币为重 { for (int i = 0; i < 3; ++i) { char * pLeft, *pRight; //指向天平两边的字符串 if (light) { pLeft = Left[i]; pRight = Right[i]; } else { // 如果假设假币是重的,则把称量结果左右对换 pLeft = Right[i]; pRight = Left[i]; } switch (result[i][0]) { //天平右边的情况 case 'u': if (strchr(pRight, c) == NULL)//天平右边翘起说明为轻的假币在右边 return false; break; case 'e': if (strchr(pLeft, c) == strchr(pRight, c))//天平平衡翘起说明为轻的假币左右两边都不能在 return false; break; case 'd': if (strchr(pLeft, c) == NULL)//天平右边下沉说明为轻的假币在左边 return false; break; } } return true; }
1.4 熄灯问题(一)
有一个由按钮组成的矩阵,其中每行有6个按钮,共5行每个按钮的位置上有一盏灯
当按下一个按钮后,该按钮以及周围位置(上边,下边,左边,右边)的灯都会改变状态
-如果灯原来是点亮的,就会被熄灭
-如果灯原来是熄灭的,则会被点亮
在矩阵角上的按钮改变3盏灯的状态
在矩阵边上的按钮改变4盏灯的状态.
其他的按钮改变5盏灯的状态
与一盏灯毗邻的多个按钮被按下时,一个操作会抵消另一次操作的结果给定矩阵中每盏灯的初始状态,求一种按按钮方案,使得所有的灯都熄灭。
输入
-第一行是一个正整数N,表示需要解决的案例数
-每个案例由5行组成,每一行包括6个数字
-这些数字以空格隔开,可以是0或1
-0表示灯的初始状态是熄灭的
-1表示灯的初始状态是点亮的
输出
-对每个案例,首先输出一行,输出字符串“PUZZLE #m”,其中m是该案例的序号
-接着按照该案例的输入格式输出5行
-1表示需要把对应的按钮按下
-0表示不需要按对应的按钮
-每个数字以一个空格隔开
解题思路
第2次按下同一个按钮时,将抵消第1次按下时所产生的结果→每个按钮最多只需要按下一次
第一想法:枚举所有可能的按钮(开关)状态,对每个状态计算一下最后灯的情况,看是否都熄灭,每个按钮有两种状态(按下或不按下),一共有30个开关,那么状态数是230,太多,会超时。
如何减少枚举的状态数目呢?
基本思路:如果存在某个局部,一旦这个局部的状态被确定,那么剩余其他部分的状态只能是确定的一种,或者不多的n种,那么就只需枚举这个局部的状态即可。
经过观察,发现第1行就是这样的一个“ 局部”
-因为第1行的各开关状态确定的情况下,这些开关作用过后,将导致第1行某些灯是亮的,某些灯是灭的
-要熄灭第1行某个亮着的灯(假设位于第i列),那么唯一的办法就是按下第2行第i列的开关
(因为第1行的开关已经用过了,而第3行及其后的开关不会影响到第1行)-为了使第1行的灯全部熄灭,第2行的合理开关状态就是唯一-的
有没有状态数更少的做法?
枚举第一列,状态数是2的5次方=32
#include <memory> #include <string> #include <cstring> #include <iostream> using namespace std; char oriLights[5]; char lights[5] ; char result[5] ; int GetBit (char c,int i){//取c的第i位 return(c>>i)&1; } void SetBit(char &c,int i ,int v){//c的第i位置为v if(v)c|=(1<<i); else c&=~(1<<i); } void FlipBit(char &c,int i){//c的第i位反转 c^=(1<<i); } void OutputResult(int t,char result[]){ cout<<"PUZZLE #"<<t<<endl; for(int i=0;i<5;++i){ for(int j;j<6;++j){ cout<<GetBit(result[i],j); if(j<5)cout<<" "; else cout<<endl; } } } int main(){ int T; for(int t=1;t<=T;++t){ for(int i=0;i<5;++i) for(int j=0;j<6;++j){ int s; cin>>s; SetBit(oriLights[i],j,s); } for(int n=0;n<64;++n){ int switchs=n; memcpy(lights,oriLights,sizeof(oriLights)); fot(int i=0;i<5;++i){ result[i]=switchs;//第i行的状态存进result for(j=0;j<6;++j){ if(GetBit(switchs,j)){ if(j>0)FlipBit(Lighis[i],j-1); FlipBit(lights[i],j); if(j<5)FlipBit(lights[i],j+1); } } if(i<5) lights[i+1]^=switchs;//下一行的灯的状态用异或即可确定 switchs=lights[i]; } if(lights[4]==0){//最后一行全被熄灭表示成功 OutputResult(t,result); break; } } } return 0; }