算法基础(一):枚举
枚举
基于逐个尝试答案的一种问题求解策略,解决问题时最先想到的搜索方法。
Problem1:假币问题
题目:有12枚硬币。其中有11枚真币和1枚假币。假币和真币重量不同,但不知道假币比真币轻还是重。现在,用一架天平称了这些币三次,告诉你称的结果,请你找出假币并且确定假币是轻还是重。
输入:第一行是测试数据组每组数据有三行,每行表示一次称量的结果。硬币A-L。称量结果分为三部分:天平左边放置的硬币 天平右边放置的硬币 平衡状态(up、down、even右相对于左) 左右硬币数相同。
输出:输出哪一个标号的硬币是假,并说明轻还是重
解题思路:枚举每一枚硬币是轻/重,看是否符合称量结果。
注:strchr函数功能为在一个串中查找给定字符的第一个匹配之处。函数原型为:char *strchr(const char *str, int c),即在参数 str 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置。strchr函数包含在C 标准库 <string.h>中。
#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;
}
}
}
return 0;
}
bool IsFake(char c,bool 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;
}
}
}
Problem2: 熄灯问题 POJ 1222
题目:5*6的灯矩阵,输入状态(1代表开,0代表灭),输出开关方案(1代表按一次开关)使得所有灯熄灭,开关按下后,四周的灯都会改变状态。
思路:每个状态都试一下,2^30次,肯定超时 。想办法缩小枚举范围!
考虑能否用枚举局部:局部确定,剩余部分确定。
枚举第一行,状态是2^6=64! -——>对应剩余行确定!(只有第二列才能改变第一列的灯,以此类推)
这里介绍一种非常好用的二进制枚举方法:枚举一个k位二进制数的每一位变化,只需要对应一个int型变量从0变到2^k-1进行枚举
即一行灯对应一个0-63的数字,对每一盏灯的操作即进行位运算操作
为了节省空间,采用char类型变量储存这个数字(只有8个比特)
技巧:异或的逻辑:跟0异或不变,跟1异或取反。
具体代码如下:
#include<iostream>
#include<string>
#include<cstring>
#include<memory>
using namespace std;
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 Flip(char& c,int i){
//将c的第i位取反 异或(跟0异或不变,跟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(){
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));
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种状态
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=lights[i]; //第i+1行开关方案和第i行灯情况相同
}
if(lights[4]==0){
outputResult(t,result);
break;
}
}
}
return 0;
}
习题解答
习题链接:http://cxsjsx.openjudge.cn/hw202012/
A:画家问题
#include<iostream>
#include<cstring>
using namespace std;
int wall[17][17];
int spread[17][17];
int n;int _count=0;int mmin=999999;
bool trytry() {
for(int i=1;i<n;i++){
for(int j=1;j<=n;j++){
spread[i+1][j]=(wall[i][j]+spread[i][j]+spread[i][j-1]+spread[i][j+1]+spread[i-1][j]+1)%2;
}
}
for(int j=1;j<=n;++j){
if((spread[n][j-1]+spread[n][j+1]+spread[n-1][j]+spread[n][j])%2==wall[n][j])
return false;
}
return true;
}
int main(){
cin>>n;
memset(wall,0,sizeof(wall));
memset(spread,0,sizeof(spread));
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
char c;
cin>>c;
if(c=='w') wall[i][j]=0;//白 0
else wall[i][j]=1;//黄 1
}
}
int flag=0;
while(1){
if(trytry()){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(spread[i][j])
_count++;
}
}
mmin=min(mmin,_count);
_count=0;
flag=1;
}
for(int i=2;i<=n;i++)
for(int j=1;j<=n;j++)
spread[i][j]=0;
spread[1][1]++;//对第一行进行枚举
int tmp=1;
while(spread[1][tmp]>1){//采用反向二进制数每次加一
spread[1][tmp]=0;tmp++;
spread[1][tmp]++;
}
if(tmp>n) break;
}
if(flag==1)
cout<<mmin<<endl;
else
cout<<"inf"<<endl;
return 0;
}
B:拨钟问题
#include<iostream>
#include<cstring>
using namespace std;
int _clock[10];int a[10][10];int use[10];int out[40];
int cplus[10];int steps;int mmin=9999;
bool trytry(){
for(int i=4;i<=9;i++){
if(i>=4&&i<=7)use[i]=(4-(_clock[i-3]+cplus[i-3])%4)%4;
else if(i==8)use[i]=(4-(_clock[7]+cplus[7])%4)%4;
else if(i==9)use[i]=(4-(_clock[5]+cplus[5])%4)%4;
for(int j=1;j<=9;j++){
cplus[j] += use[i]*a[i][j];
}
}
for(int i=1;i<=9;i++){
if((cplus[i]+_clock[i])%4!=0) return false;
}
steps=0;
for(int i=1;i<=9;i++) steps+=use[i];
return true;
}
int main(){
for(int i=1;i<=9;i++){
int t;cin>>t;
_clock[i]=t;
}
memset(a,0,sizeof(a));
memset(out,0,sizeof(out));
memset(cplus,0,sizeof(cplus));
for(int i=1;i<=9;i++){
switch (i)
{
case 1:
a[i][1]=1;a[i][2]=1;a[i][4]=1;a[i][5]=1;break;
case 2:
a[i][1]=1;a[i][2]=1;a[i][3]=1;break;
case 3:
a[i][2]=1;a[i][3]=1;a[i][5]=1;a[i][6]=1;break;
case 4:
a[i][1]=1;a[i][4]=1;a[i][7]=1;break;
case 5:
a[i][2]=1;a[i][4]=1;a[i][5]=1;a[i][6]=1;a[i][8]=1;break;
case 6:
a[i][3]=1;a[i][6]=1;a[i][9]=1;break;
case 7:
a[i][4]=1;a[i][5]=1;a[i][7]=1;a[i][8]=1;break;
case 8:
a[i][7]=1;a[i][8]=1;a[i][9]=1;break;
case 9:
a[i][5]=1;a[i][6]=1;a[i][8]=1;a[i][9]=1;break;
}
}
for(int i=0;i<=3;i++)
for(int j=0;j<=3;j++)
for(int k=0;k<=3;k++){
use[1]=i;use[2]=j;use[3]=k;
for(int m=1;m<=3;m++){
for(int n=1;n<=9;n++){
cplus[n]+=use[m]*a[m][n];
}
}
if(trytry()&&steps<mmin){
mmin=steps;
int tmp=0;
for(int i=1;i<=9;i++){
for(int j=1;j<=use[i];j++)
out[++tmp]=i;
}
break;
}
for(int m=1;m<=9;m++){
use[m]=0;cplus[m]=0;
}
}
cout<<out[1];
for(int i=2;out[i]!=0;i++) cout<<" "<<out[i];
cout<<endl;
return 0;
}
C:特殊密码锁
#include<iostream>
#include<cstring>
using namespace std;
int main(){
int a[32];int b[32];int press[32];int now[32];int step;int mmin=999;int flag=0;
string s1,s2;
cin>>s1>>s2;
for(int i=1;i<=s1.length();i++){
a[i]=s1[i-1]-'0';now[i]=a[i];
b[i]=s2[i-1]-'0';
}
for(int t=0;t<=1;t++){
memset(press,0,sizeof(press));
press[1]=t;
step=t;
now[1]^=t;now[2]^=t;
for(int i=2;i<=s1.length();i++){
if(now[i-1]!=b[i-1]){
press[i]=1;step++;
}
now[i]^=press[i];
now[i-1]^=press[i];
now[i+1]^=press[i];
if(i==s1.length()&&now[i]==b[i]){
mmin=min(mmin,step);
flag=1;
}
}
for(int i=1;i<=s1.length();i++) now[i]=a[i];
}
if(flag) cout<<mmin<<endl;
else cout<<"impossible"<<endl;
return 0;
}

浙公网安备 33010602011771号