/**
* 题目名称:费解的开关
*
* 题目链接:https://www.acwing.com/problem/content/description/97/
*
* 考点:递推
*
* 解题思路:递推的体现是由上一行的状态推出下一行要采取的操作
* 因为每操作一个点带来的影响都是上下左右四个方向,所以当我们
* 只考虑一个方向时情况会变得很简单,比如只考虑上下方向或左右方向
* 也就是上一行某点的灯是关着的,如果我们想打开它,方法就是操作它下方的点
* 或者左一列某点的灯是关着的,如果我们想打开它,方法就是操作它右方的点
* 这么做的好处就是把上下左右点的变化转化为只考虑上下或者左右
*
* 代码实现(选择上下方向):从上面的解题思路可以看出,下一行的操作是受到上一行情况的约束的
* 这里有两个特殊的行:第0行和第4行,前者没有上一行的约束,后者没有下一行可以操作来改变自己的状态
* 对于第0行的处理,因为没有约束条件,所以怎么操作都是有可能的,即需要遍历所有情况,每盏灯有操作和不操作两种选择,一共5盏灯,共2^5种,代码中使用二进制来表示每一种状态
* 对于第4行,因为没有下一行,所以状态无法再改变,即它的状态决定了本种情况是否合法
*
* 易错点:1.不是只按关着的灯 2.任意一行任意一盏灯都可以操作,而第0行又没有约束条件,所以第0行就需要遍历所有情况
*/
#include <iostream>
#include <cstring>
using namespace std;
const int INF = 0x7f7f7f7f;
int xx[] = {0, -1, 0, 1, 0};
int yy[] = {0, 0, 1, 0, -1};
char a[10][10];
void turn(int x, int y)
{
for(int i = 0; i < 5; ++i)
{
int dx = x + xx[i];
int dy = y + yy[i];
if (dx >= 0 && dx < 5 && dy >= 0 && dy < 5)
a[dx][dy] ^= 1;
}
}
int work()
{
int ans = INF;
for (int k = 0; k <= (1 << 5); ++k) // 遍历第一行的所有可能的操作,二进制为1的位表示要按
{
int ret = 0;
char tmp[10][10];
memcpy(tmp, a, sizeof(a)); // 因为这里只是试探这么一种情况,所以需要保留原状态
// 修改第一行的状态
for (int j = 0; j < 5; ++j)
if(k >> j & 1)
{
turn(0, j);
++ret;
}
// 递推根据上一行的状态确定下一行的状态
for (int i = 0; i < 4; ++i)
for(int j = 0; j < 5; ++j)
if(a[i][j] == '0')
{
turn(i + 1, j);
++ret;
}
// 判定情况是否合法
bool is_successful = true;
for (int j = 0; j < 5; ++j)
if (a[4][j] == '0')
{
is_successful = false;
break;
}
if (is_successful) ans = min(ans, ret);
memcpy(a, tmp, sizeof(a)); // 恢复原状态
}
if(ans > 6) return -1;
return ans;
}
int main()
{
int T;
cin >> T;
while (T--)
{
for (int i = 0; i < 5; ++i) cin >> a[i];
cout << work() << endl;
}
return 0;
}
费解的开关
最新推荐文章于 2023-05-25 16:55:24 发布