枚举

定义

逐个尝试答案

例题1:完美立方

解题思路:四重循环枚举a,b,c,d,a在最外边,d在最里边,每一层都是从小到大枚举,a范围是[2,N],b范围是[2,a-1],c范围是[b,a-1],d范围是[c,a-1]

#include <iostream>
#include <cstdio>
using namespace std;
int main(){
	int N;
	scanf("%d",&N);
	int a,b,c,d;
	for(a=2;a<=N;a++){
		for(b=2;b<a;b++){
			for(c=b+1;c<a;c++){
				for(d=c+1;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);
					}
				}
			}
		}
	}
} 

例题2:生理周期 

解题思路:从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,k,CaseNum=0;
	while(cin >>p>>e>>i>>d){
	CaseNum++;
	//找到体力相隔23天的日子(即体力高峰期) 
	//for语句condition如果为真,则执行循环主体。如果为假,则不执行循环主体
	for(k=d+1;(k-p)%23;k++);
	//在体力高峰期找到情商相隔28天的日子(即情商高峰期)
	for(;(k-e)%28;k+=23);
	//在体力,情商高峰期找智商高峰期 
	for(;(k-i)%33;k+=23*28);
	cout << "Case "<<CaseNum<<": the next triple peak occurs in "<<k-d<<" days."<<endl;		
	}
	return 0;

}

例题3:称硬币
 

解题思路
对于每一枚硬币先假设它是轻的,看这样是否符合称 量结果。如果符合,问题即解决。如果不符合,就假 设它是重的,看是否符合称量结果。把所有硬币都试 一遍,一定能找到特殊硬币

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
char Left[3][7];//天平左边硬币 
char Right[3][7];//天平右边硬币
char result[3][7];//天平右边结果 even up down 
bool IsFake(char c,bool light);//light表示假币为轻
int main(){
	int i;//测试数据的组数 
	cin >>i;
	while(i--){
		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;
			}
		}
	} 
	return 0;
} 
bool IsFake(char c,bool light){//light 为真表示假设假币为轻
	for(int i=0;i<3;i++){
		switch(result[i][0]){
			case 'u':
				if(strchr(Right[i],c)==NULL)//NULL不是Null 
					return false;
					break;
			case 'e':
				if(strchr(Left[i],c) || strchr(Right[i],c))
					return false;
					break;
			case 'd':
				if(strchr(Left[i],c)==NULL)
					return false;
					break;
		} 
	}
	return true;
}

例题4:熄灯问题

解题分析 

  • 第2次按下同一个按钮时, 将抵消第1次按下时所产生的结果 -->每个按钮最多只需要按下一次
  • 各个按钮被按下的顺序对最终的结果没有影响
  • 对第1行中每盏点亮的灯, 按下第2行对应的按钮, 就可以 熄灭第1行的全部灯 
  • 如此重复下去, 可以熄灭第1, 2, 3, 4行的全部灯
  •  第一想法: 枚举所有可能的按钮(开关)状态, 对每个状态计 算一下最后灯的情况, 看是否都熄灭 – 每个按钮有两种状态(按下或不按下) – 一共有30个开关, 那么状态数是2的30次方, 太多, 会超时
  • 如何减少枚举的状态数目呢? 基本思路: 如果存在某个局部, 一旦这个局部的状态被确定, 那么剩余其他部分的状态只能是确定的一种, 或者不多的n 种, 那么就只需枚举这个局部的状态即可 
  • 本题是否存在这样的 “局部” 呢?
  • 经过观察, 发现第1行就是这样的一个 “局部” – 因为第1行的各开关状态确定的情况下, 这些开关作用过后, 将 导致第1行某些灯是亮的, 某些灯是灭的 --->要熄灭第1行某个亮着的灯(假设位于第i列), 那么唯一的办法就 是按下第2行第i列的开关 (因为第1行的开关已经用过了, 而第3行及其后的开关不会影响 到第1行) – 为了使第1行的灯全部熄灭, 第2行的合理开关状态就是唯一的 --->第2行的开关起作用后, 为了熄灭第2行的灯, 第3行的合理开关状态就也是唯一的 ---> 以此类推, 最后一行的开关状态也是唯一的 ---->只要第1行的状态定下来,  那么剩余行的情况就是确定唯一 的了 --->推算出最后一行的开关状态, 然后看看最后一行的开关起作用后, 最后一行的所有灯是否都熄灭: 如果是, 那么A就是一个解的状态 • 如果不是, 那么A不是解的状态, 第1行换个状态重新试试 • 只需枚举第1行的状态, 状态数是2的6次方 = 64
     

 

#include <memory> 
#include <string> 
#include <cstring> 
#include <iostream> 
#include <cstdio> 
using namespace std; 
int GetBit(char c,int i){
	//取c的第i位  
	return (c>>i)&1;
}
void SetBit(char & c,int i,int s){
	//设置c的第i位状态是s(0/1)
	if(s)
		//或,常用来置1 
		c |= (1<<i);
	else
		//与,常用来置0 
		c &= ~(1<<i);
} 
void Flip(char & c,int i){
	//异或,不相同时为1,常用取反 
	c ^= ( 1 << i);  
} 
void OutputResult(int t,char result[]){
	//输出结果
	cout << "PUZZLE #" << t << endl; 
	for(int i=0;i<5;i++){
		for(int j=0;j<6;j++){
			cout <<GetBit(result[i],j);
			if(j<5)cout << " ";//输出空格 
		}
		cout <<endl;
	} 
} 
int main(){
	freopen("F:\\Dev-cpp5.4.0及API帮助文档\\test\\meiju\\xideng.txt","r",stdin);
	char oriLights[5];//最初灯矩阵,一个比特表示一盏灯
	char Lights[5];//不停变化的灯矩阵
	char result[5];//结果开关矩阵 
	char switchs; //某一行的开关状态  
	int T;
	cin >>T;//需要解决的案例数
	for (int t=1;t<=T;t++){
		memset(oriLights,0,sizeof(oriLights)); //先全部填充为0
		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++){//遍历首行64种状态 
			//每次先把原始状态放入lights中 
			memcpy(Lights,oriLights,sizeof(oriLights));
		 	switchs = n;//第i行的开关状态
			for(int i=0;i<5;i++){
				result[i]=switchs;//第i行的开关方案(已经确定) 
				for(int j=0;j<6;j++){
					if(GetBit(switchs,j)){
						//开关被按下,才需要处理
						if(j>0)Flip(Lights[i],j-1);//改左灯
						Flip(Lights[i],j);//改开关位置的灯 
						if(j<5)Flip(Lights[i],j+1);//改右灯 
						
					} 
				}
				//改下一行的灯,因为上一行会对下一行有影响 
				if(i<4)Lights[i+1] ^=switchs;
				//switchs是第i+1行的开关状态
				switchs = Lights[i]; 
			} 
			if(Lights[4]==0){
				OutputResult(t,result); break;
			}
		} 
	} 
} 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值