费解的开关

/**
 * 题目名称:费解的开关
 * 
 * 题目链接: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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值