一、枚举
现实生活中有很多问题是没有策略或公式的,只能一个个枚举
1. 完美立方
形如a3=b3+c3+d3a^3=b^3+c^3+d^3a3=b3+c3+d3的等式被称为完美立方,编写一个程序,对任给的正整数N(N<=100),寻找所有的四元组(a,b,c,d),使得满足完美立方,其中a,b,c,d大于1小于等于N,且b<=c<=d。
输入:一个正整数(N<=100)。
输出:每行输出一个完美立方。输出格式为:
Cube = a, Triple = (b,c,d)
样例输入:
24
样例输出:
Cube = 6, Triple = (3,4,5)
Cube = 12, Triple = (6,8,10)
Cube = 18, Triple = (2,12,16)
Cube = 18, Triple = (9,12,15)
Cube = 19, Triple = (3,10,18)
Cube = 20, Triple = (7,14,17)
Cube = 24, Triple = (12,16,20)
解题思路:a的枚举范围[2,N],b范围[2,a-1],c范围[b,a-1],d范围[c,a-1]。
C++:
#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;
}
Python:
#传入一个数,求它的完美立方
import numpy as np
def wanmei_lifang(N):
for a in range(2,N+1):
for b in range(2,a):
for c in range(b,a):
for d in range(c,a):
#if a*a*a==b*b*b+c*c*c+d*d*d:
if np.power(a,3)==np.sum(np.power([b,c,d],[3,3,3])):
print("Cube = {0},Triple = ({1},{2},{3})" .format(a,b,c,d))
if __name__=="__main__":
N=int(input())
wanmei_lifang(N)
2. 生理周期
人有体力、情商、智商的高峰日子,他们分别每隔23天、28天和33天出现一天。对于每个人,我们想知道何时三个高峰落在同一天。给定三个高峰出现的日子p,e和i(不一定是第一次高峰出现的日子),在给定另一个指定的日子d,你的任务是输出日子d之后,下一次三个高峰落在同一天的日子(用距离d的天数表示),例如,给定日子是10,下次出现三个高峰同一天的日子是12,则输出2。
输入:
输入四个整数:p、e、i和d,分别表示体力、情商、智商出现的日子,d是给定的日子。若输入4个-1则结束。
输出:
从给定日子起,下一次三个高峰同一天的日子的天数。
例如:
输入:
0 0 0 0
0 0 0 100
-1 -1 -1 -1
输出:
Case 1:the next triple peak occurs in 21252 days.
Case 2:the next triple peak occurs in 21152 days.
解题思路:
- 从d+1天开始,一直试到第21252天,对其中每个日子k,看是否满足
(k-p)%23=0 && (k-e)%28=0 && (k-i)%33=0,循环次数太多,如何更快???
- 跳着试,先找到一个体力高峰的日子,每隔一个体力高峰的周期做尝试;看是否是体力情商高峰的共同日子;再每个体力情商双高峰为循环,找智商高峰的日子。
C++:
#include <iostream>
#include <cstdio>
using namespace std;
#define N 21252;
//生理周期
int main(){
int p,e,i,d,count=0;
while(cin>>p>>e>>i>>d && p!=-1){
count++;
int k;
for(k=d+1;(k-p)%23;k++);
cout<<k<<endl;
for(;(k-e)%28;k+=23);
cout<<k<<endl;
for(;(k-i)%28*23;k+=23*28);
cout<<k<<endl;
cout<<"Case "<<count<<"the next triple occour in "<<k-d;
}
return 0;
}
Python:
#传入一个数,求它的完美立方
N=21252
count=0
p,e,i,d=list(map(int,input().split(" ")))
while (p!=-1):
count+=1
k=d+1
while ((k-p)%23):
k+=1
while ((k-i)%28):
k+=23
while ((k-e)%23):
k+=23*28
print("Case {0}:the next triple peak occurs in {1} days.".format(count,k-d))
p,e,i,d=list(map(int,input().split(" ")))
3. 称硬币
有12枚硬币。其中有11枚真币和1枚假币。假币和真币重量不同,但不知道假币比真币轻还是重。现在,用一架天平称了这些硬币三次,告诉你称的结果,请你找出假币并且确定假币是轻是重(数据保证一定能找出来)。
输入数据:
1
ABCD EFGH even
ABCI EFJK up
ABIJ EFGH even
输出样例:
K is the counterfeit coin and it is light.
解题思路:
对于每一枚硬币先假设它是轻的,看这样是否符合称量结果。如果符合,问题即解决。如果不符合,就假设它是重的,看是否符合称量结果。把所有硬币都试一遍,一定能找到特殊硬币。
C++:
#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为真表示为轻,否则为重
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;
}
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;
}
}
}
}
Python:
#称硬币
#赋予初值
Left=["ABCD","ABCD","ABCD"]
Right=["ABCD","ABCD","ABCD"]
Result=["even","up","even"]
def IsFake(c,is_light):
for j in range(3):
if (is_light):
Left_=Left
Right_=Right
else:
Left_=Right
Right_=Left
if (Result[j][0]=="d"):
if (c not in Left_[j]):
return False
elif (Result[j][0]=="e"):
if (c in Left_[j]) or (c in Right_[j]):
return False
elif (Result[j][0]== "u"):
if (c not in Right_[j]):
return False
return True
if __name__=="__main__":
t=int(input())
print("t:",t)
while(t>0):
Left[0],Right[0],Result[0]=list(map(str,input().split(" ")))
Left[1],Right[1],Result[1]=list(map(str,input().split(" ")))
Left[2],Right[2],Result[2]=list(map(str,input().split(" ")))
c="ABCDEFGHIJKL"
for i in c:
if (IsFake(i,True)):
print(i," is the counterfeit coin and it is light.\n")
break
elif (IsFake(i,False)):
print(i," is the counterfeit coin and it is heavy.\n")
break
t=t-1
4. 熄灯问题
有一个由按钮组成的矩阵,其中每行有6个按钮,共5行;
每个按钮的位置上有一盏灯;
当按下一个按钮后,该按钮以及周边位置(上边,下边,左边,右边)的灯都会改变状态;若在边界,则改变周边三个位置的灯的状态;
如果灯原来是亮的,就会被熄灭;如果灯原来是灭的,就会被点亮。
给定矩阵中每盏灯的初始状态,求一种按按钮方案,使得所有的灯都会熄灭。
输入:
第一行是一个正整数N,表示需要解决的案例数
每个案例由5行组成,每一行包括6个数字
这些数字以空格隔开,可以是0或1
0表示灯的初始状态是熄灭的;1表示灯的初始状态是点亮的
例如:
输入:
1
0 1 1 0 1 0
1 0 0 1 1 1
0 0 1 0 0 1
1 0 0 1 0 1
0 1 1 1 0 0
输出:
PUZZLE #1
1 0 1 0 0 1
1 1 0 1 0 1
0 0 1 0 1 1
1 0 0 1 0 0
0 1 0 0 0 0
解题思路:
一个开关最多按一次(因为按两次是没有意义的,等于没有按);
- 枚举所有可能的按钮状态,但是太多了,2302^{30}230次,太多,会超时;
- 枚举某一个局部,该局部一旦确定,剩余部分的状态都被确定了,本题中的局部就是第一行。第一行一旦确定,剩下的几行就都确定。因为第一行确定了,这些开关作用后,就会导致第1行某些灯是亮的,某些灯是灭的,要熄灭第一行亮着的灯(假设位于第i列),那么唯一的办法就是按下弟2列第i列的开关(因为第1行的开关已经用过了,而第3行以及之后的开关不会影响到第1行);为了使得第一行的灯全部熄灭,第2行的合理开关状态就是唯一的
C++:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <memory>
using namespace std;
char oriLight[5];//一个char是8个比特
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);//将c的第i位变成1
}
else{
c &= ~(1<<i);//将c的第i位变成0
}
}
void FlipBit(char & c,int i){
//翻转
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(){
int T;
cin>>T;
for (int t=0;t<T;t++){
for (int i=0;i<5;i++){
for (int j=0;j<6;j++){
int s;
cin>>s;
SetBit(oriLight[i],j,s);
}
}
//枚举第一行所有可能的状态
for (int n=0;n<64;n++ ){
int switchs=n;
memcpy(lights,oriLight,sizeof(oriLight));
for (int i=0;i<5;++i){
result[i]=switchs;
for (int j=0;j<6;j++){
if(GetBit(switchs,j)){
if (j>0)
FlipBit(lights[i],j-1);
FlipBit(lights[i],j);
if (j<5)
FlipBit(lights[i],j+1);
}
}
if (i<4)
lights[i+1] ^=switchs;
//如果switchs的某一位是1,则需要翻转第下一行的该位
switchs=lights[i];
}
if (lights[4]==0){
OutputResult(t,result);
break;
}
}
}
return 0;
}
Python:
import numpy as np
#初始化
oriLight=np.zeros((5,6))
lights=np.zeros((5,6))
result=np.zeros((5,6))
def decToBin(n):#十进制转二进制,二进制是6位,不足补0,返回一个int型的列表
arry=[]
while True:
arry.append(str(n%2))
n=n//2
if n==0:
break
for i in range(6-len(arry)):
arry.append("0")
return np.array(list(map(int,arry[::-1])))
#return "".join(array[::-1])
if __name__=="__main__":
T=int(input("请输入T:"))
for t in range(T):
#输入原始矩阵,空格间隔
for x in range(5):
s=list(map(int,input().split(" ")))
oriLight[x]=np.array(s)
#print(oriLight) #打印原始矩阵
for n in range(64):
switchs=decToBin(n) #固定第一行,则下面的行都确定了
lights=oriLight.copy()#复制原始矩阵
#print(lights)
for i in range(5):
result[i]=switchs
for j in range(6):
if int(switchs[j]):
if (j>0):
lights[i][j-1]=1-int(lights[i][j-1])#翻转本行的上一列的数
lights[i][j]=1-int(lights[i][j])#翻转本行本列
if (j<5):
lights[i][j+1]=1-int(lights[i][j+1])#翻转本行的下一列的数
if (i<4):#翻转下一行的数
for z in range(len(switchs)):
lights[i+1][z]=1-int(lights[i+1][z]==switchs[z])
switchs=lights[i]
if (lights[4].sum()==0):#判断最后一行是否都是0,若是,则满足,输出结果矩阵
print("PUZZLE:#",t)
print(result)
break
课程地址:中国大学MOCC网,郭炜,程序设计语算法(二)算法基础