SCU2016-02 J题 (开关反转)

本文介绍了一种解决特定棋盘翻转问题的方法。通过枚举第一行的所有可能翻转状态并递归地确定后续行的状态,找到所需的最少翻转次数以达到目标状态。文章详细展示了算法实现过程及代码。

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

分析:

先确保最左上角的正确,然后确保第一行,最后一次递推接下来的行。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define pr(x) cout << #x << ": " << x << "  " 
#define pl(x) cout << #x << ": " << x << endl;
int M, N;
bool tile[20][20]; // 输入棋盘状态,1为黑色,0为白色
bool flip[20][20]; // 翻转状态
bool opt[20][20]; // 最优解
// 邻接格子坐标
int dx[] = {0, -1, 1, 0, 0};
int dy[] = {0, 0, 0, 1, -1};
void show_tile(bool t[20][20]) {
    for(int i=0; i<M; ++i) {
        for(int j=0; j<N; ++j)
            printf("%d ", t[i][j]);
        printf("\n");
    }
}
bool get_color(int i, int j) { // 获得(i, j)格子的颜色,黑色返回1,白色返回0
    int c = tile[i][j];
    for(int d=0; d<5; ++d) {
        int nx = i+dx[d], ny = j+dy[d];
        if(nx >= 0 && nx < M && ny >=0 && ny < N) c+=flip[nx][ny];
    }
    return c&1;
}
int calc() { // 计算第一行确定后,所需要的步数,不存在返回-1
    for(int i=1; i<M; ++i)
        for(int j=0; j<N; ++j)
            if(get_color(i-1, j))  // 上一个格子为黑色的话,需要翻转
                flip[i][j] = 1;
    for(int j=0; j<N; ++j)  // 判断最后一行是否全为0
        if(get_color(M-1, j)) return -1;
    int res = 0;
    for(int i=0; i<M; ++i)
        for(int j=0; j<N; ++j) if(flip[i][j]) ++res;
    return res;
}
void solve() {
    int res = -1;
    for(int i=0; i<(1<<N); ++i) { // 枚举第一行所有翻转状态,有2^N种情况
        memset(flip, false, sizeof(flip));
        for(int j=0; j<N; ++j)
            flip[0][N-j-1] = (i >> j) & 1; // 这题字典序从右往左计算= =
        int num = calc();
        if(num >= 0 && (res < 0 || res > num)) {
            res = num;
            memcpy(opt, flip, sizeof(flip));
        }
    }
    if(res == -1) puts("IMPOSSIBLE");
    else show_tile(opt);
}
int main() {
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
#endif
    cin >> M >> N;
    for (int i = 0; i < M; ++i)
        for (int j = 0; j < N; ++j)
            cin >> tile[i][j];
    solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值