程序设计与算法(二)_枚举

本文探讨了枚举算法在解决特定问题中的应用,如完美立方、生理周期预测、硬币称重谜题及熄灯问题。通过C++和Python实现,展示了如何通过遍历所有可能性来寻找解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、枚举

现实生活中有很多问题是没有策略或公式的,只能一个个枚举

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.

解题思路:

  1. 从d+1天开始,一直试到第21252天,对其中每个日子k,看是否满足

(k-p)%23=0 && (k-e)%28=0 && (k-i)%33=0,循环次数太多,如何更快???

  1. 跳着试,先找到一个体力高峰的日子,每隔一个体力高峰的周期做尝试;看是否是体力情商高峰的共同日子;再每个体力情商双高峰为循环,找智商高峰的日子。

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行;

每个按钮的位置上有一盏灯;

当按下一个按钮后,该按钮以及周边位置(上边,下边,左边,右边)的灯都会改变状态;若在边界,则改变周边三个位置的灯的状态;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kO6YbdHD-1583504608909)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20200305165014917.png)]

如果灯原来是亮的,就会被熄灭;如果灯原来是灭的,就会被点亮。

给定矩阵中每盏灯的初始状态,求一种按按钮方案,使得所有的灯都会熄灭。

输入:

第一行是一个正整数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

解题思路:

一个开关最多按一次(因为按两次是没有意义的,等于没有按);

  1. 枚举所有可能的按钮状态,但是太多了,2302^{30}230次,太多,会超时;
  2. 枚举某一个局部,该局部一旦确定,剩余部分的状态都被确定了,本题中的局部就是第一行。第一行一旦确定,剩下的几行就都确定。因为第一行确定了,这些开关作用后,就会导致第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网,郭炜,程序设计语算法(二)算法基础

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值