P6201 & P1985 Fliptile S 题解

Fliptile S & 翻转棋 Fliptile S

暴力-47pts

搜索每个格子翻或不翻,最后判断是否每个点都为零即可

点击查看代码
#include <bits/stdc++.h>

using namespace std;

const int dx[5] = {-1, 0, 0, 1, 0}, dy[5] = {0, -1, 1, 0, 0};

int n, m, a[20][20], ans[400], b[20][20], c[400], ansum;
bool f;

bool check(){
  for (int i = 1; i <= n; i++){
    for (int j = 1; j <= m; j++){
      if (b[i][j]){
        return 0;
      }
    }
  }
  return 1;
}

void dfs(int x, int y, int t, int sum){
  if (sum > ansum){
    return ;
  }
  if (f && sum == ansum && c[t - 1] > ans[t - 1]){
    return ;
  }
  if (x == n && y == m + 1){
    if (check()){
      if (!f || sum < ansum){
        for (int i = 1; i < t; i++){
          ans[i] = c[i];
        }
        f = 1;
        return ;
      }
      for (int i = 1; i < t; i++){
        if (c[i] < ans[i]){
          for (int j = 1; j < t; j++){
            ans[i] = c[i];
          }
          break;
        } else if (c[i] > ans[i]){
          break;
        }
      }
    }
    return ;
  }
  if (y == m + 1){
    dfs(x + 1, 1, t, sum);
    return ;
  }
  for (int i = 0; i < 5; i++){
    b[x + dx[i]][y + dy[i]] = !b[x + dx[i]][y + dy[i]];
  }
  c[t] = 1;
  dfs(x, y + 1, t + 1, sum + 1);
  for (int i = 0; i < 5; i++){
    b[x + dx[i]][y + dy[i]] = !b[x + dx[i]][y + dy[i]];
  }
  c[t] = 0;
  dfs(x, y + 1, t + 1, sum);
}

int main() {
  cin >> n >> m;
  ansum = n * m + 1;
  for (int i = 1; i <= n; i++){
    for (int j = 1; j <= m; j++){
      cin >> a[i][j];
      b[i][j] = a[i][j];
    }
  }
  dfs(1, 1, 1, 0);
  if (!f){
    cout << "IMPOSSIBLE";
  } else {
    for (int i = 1; i <= n * m; i++){
      cout << ans[i] << ' ';
      if (i % m == 0){
        cout << '\n';
      }
    }
  }
  return 0;
}

正解-100pts

首先,在字典序最小的情况下,翻的次数只有两种

  1. 翻奇数次相当于翻 \(1\)
  2. 翻偶数次相当于翻 \(0\)

当第一排固定之后,后面翻或不翻都是固定的。

  • 当第一排固定后,如果上一行的对应位置为 \(1\),那么当前位置 必须 翻一次,依此类推

最后只需判断第 \(M\) 行是否全为零即可。

点击查看代码
#include <bits/stdc++.h>

using namespace std;

const int dx[5] = {-1, 0, 0, 1, 0}, dy[5] = {0, -1, 1, 0, 0};

int n, m, a[20][20], ans[400], b[20][20], leb[20][20], c[400], sum, ansum;
bool f;

void s(int x, int sum){
  if (x == m + 1){
    int num = 0, t = m + 1;
    for (int i = 1; i <= n; i++){
      for (int j = 1; j <= m; j++){
        leb[i][j] = b[i][j];
      }
    }
    // 之后每行都是固定的
    for (int i = 2; i <= n; i++){
      for (int j = 1; j <= m; j++){
        if (b[i - 1][j]){
          num++, c[t] = 1;
          for (int k = 0; k < 5; k++){
            b[i + dx[k]][j + dy[k]] = !b[i + dx[k]][j + dy[k]];
          }
        } else {
          c[t] = 0;
        }
        t++;
      }
    }
    bool flag = 1;
    for (int i = 1; i <= m; i++){
      if (b[n][i]){ // 有内鬼!
        flag = 0;
        break;
      }
    }
    if (flag && sum + num < ansum){ // 没有内鬼并且翻的次数少些
      f = 1;
      ansum = sum + num;
      for (int i = 1; i <= n * m; i++){
        ans[i] = c[i];
      }
    }
    for (int i = 1; i <= n; i++){
      for (int j = 1; j <= m; j++){
        b[i][j] = leb[i][j];
      }
    }
    return ;
  }
  s(x + 1, sum); // 注意!先选不翻的情况才能使字典序最小
  // 翻!
  for (int j = 1; j < 5; j++){
    b[1 + dx[j]][x + dy[j]] = !b[1 + dx[j]][x + dy[j]];
  }
  c[x] = 1;
  s(x + 1, sum + 1);
  // 回溯
  c[x] = 0;
  for (int j = 1; j < 5; j++){
    b[1 + dx[j]][x + dy[j]] = !b[1 + dx[j]][x + dy[j]];
  }
}

int main() {
  cin >> n >> m;
  ansum = n * m + 1;
  for (int i = 1; i <= n; i++){
    for (int j = 1; j <= m; j++){
      cin >> a[i][j];
      b[i][j] = a[i][j];
    }
  }
  s(1, 0);
  if (!f){ // 不成立
    cout << "IMPOSSIBLE";
  } else {
    for (int i = 1; i <= n * m; i++){
      cout << ans[i] << ' ';
      if (i % m == 0){
        cout << '\n';
      }
    }
  }
  return 0;
}
posted @ 2023-05-17 20:32  wnsyou  阅读(21)  评论(0编辑  收藏  举报