枚举(算法基础)(含熄灯问题)

枚举是基于逐个尝试答案的一种解题策略。

例题一:完美立方

#include<iostream>
using namespace std;
int main()
{
	int n;
	cin >> 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)
					{
						cout << "Cube = " << a << ", Triple = (" << b << "," << c << "," << d << ")" << endl;
					}
				}
			}
		}
	}
	system("pause");
}

例题二:生理周期

#include<iostream>
using namespace std;
int main()
{
	constexpr int N = 21252;
	int p, e, i, d, times = 0;
	while (cin >> p >> e >> i >> d)

	{
		++times;
		int k;
		for (k = d + 1; (k - p) % 23; ++k);
		for (; (k - e) % 28; k += 23);
		for (; (k - i) % 33; k += 28*23); //28和23的最小公倍数即是乘积
		cout << "Case " << times << ": the next triple peak occurs in " << k - d << endl;
	}
	system("pause");
}

例题三、称硬币

#include<iostream>
using namespace std;
char Left[3][7];   //[3][7]是因为三组称量结果,且硬币共12枚,一边最多6枚
char Right[3][7];   
char Result[3][5];
bool isFake(char c, bool light)    //判断假币。light若true则是假设为轻的假币,反之假设为重
{
	char *pleft, *pright;
	for (int i = 0; i != 3; ++i)
	{
		if (light)
		{
			pleft = Left[i];
			pright = Right[i];
		}
		else   //重假币,则指向相反,便可不用再写一遍下面的switch
		{
			pleft = Right[i];
			pright = Left[i];
		}
		switch (Result[i][0])  //天平右边结果的三种情况up even和down
		{    //以假设为轻假币解释
		case 'u':	//up说明轻假币应该在右边
			if (strchr(pright, c) == NULL)   //若c不在右边说明假设错误
				return false;
			break;
		case 'e':  //若天平平衡说明假设的c不应该在天平的左右侧
			if(strchr(pleft,c)||strchr(pright,c))
				return false;
			break;
		case 'd':   //同case ‘u’
			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 fake one and it is light." << endl;
				break;
			}
			else if (isFake(c, false))   //假设重
			{
				cout << c << " is the fake one and it is heavy." << endl;
				break;
			}
		}
	}
	system("pause");
}

例题四、熄灯问题

#include<iostream>
using namespace std;
char orlights[10];    //因为数据是只含0或1,用char的一维数组位运算比用二维数组简单快捷的多
char lights[10];
char result[10];
int GetBit(char c, int i)  //取出字符c第i位的比特
{
	return (c >> i) & 1;  
}
void SetBit(char & c, int i, int v)  //将c第i位改成v其余不变 注意c是引用,因为在C++中用引用可以在函数中修改形参时传进来的实参也跟着改变
{
	if (v)
		c |= (1 << i);
	else
		c &= ~(1 << i);
}
void FlipBit(char & c, int i) //将c的第i位翻转
{
	c ^= (1 << i);  //因为跟0异或不变,跟1异或翻转
}
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;     //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(orlights[i], j, s); //将输入的整数s设为i行j列的比特
			}
		}
		for (int n = 0; n != 64; ++n) 
        //二进制枚举法(将一个整数k从0递增到k-1,每个数都可以表示k位二进制的一种情况,因为其二进制所表达的数是从0到2的k-1次方) 枚举第一行可能的情况 因为有6个数所以2的6次方=64
		{
			int switchs = n;
			memcpy(lights, orlights, sizeof(orlights));
			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)
							FlipBit(lights[i], j - 1);
						FlipBit(lights[i], j);
						if (j < 5)
							FlipBit(lights[i], j + 1);
					}
				}
				if (i < 4)
					lights[i + 1] ^= switchs;   //因为若第i行第j个比特是1则是按下开关则要翻转,而与1异或就是翻转,是0就不用
				switchs = lights[i];  //i行灯的状态与i+1行灯开关的按法是一致的 这样才能熄灭
			}
			if (lights[4] == 0)  //只要最后一行灯是灭的则可说明此种情况是全灭
			{
				OutputResult(t, result); //输出并跳出
				break;
			}
		}
	}
	system("pause");
}

猜你喜欢

转载自blog.csdn.net/weixin_44009743/article/details/87927009