洛谷P1764 翻转游戏(加强版)

题目

https://www.luogu.com.cn/problem/P1764

思路

其实这是当年高三时模拟赛的一道题,当时写了个假的贪心水了40分,结果教练让我上去讲题,就不得不学了一下。

后来看电视节目的时候发现这个题的变种在《最强大脑》里出现了,也算是一个经典的模型吧。

这道题其实需要一点意会。。。(描述简单起见,将翻转第x行第y列方格为中心的十字这个操作称为\(flip(x,y)\),将方阵称为\(a\),第\(i\)行第\(j\)列称为\(a[i][j]\)

很显然这个翻转操作是会影响上面一行的,乍看决策之间是相互影响的。但是,考虑到第一行不存在上一行,我们珂以 \(2^n\) 枚举第一行的每一格是否执行\(flip\)操作。

不妨以改成全白为例:

逐行处理,对于\(1到n-1\)行,挨个检查\(a[i][j]\)是否为白色,如果为白色不操作,如果为黑色执行\(flip(i+1,j)\)(为什么不能是\(flip(i,j)\)?因为这会导致我们之前已经钦定好的格子发生改变)

对于第\(n\)行,如果不是全白,那么说明第一行的该决策无可行解,直接返回\(inf\)

简而言之,就是说只要第一行的决策确定,那么整个方阵的最优解就确定了。

代码

点击查看代码
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
char a[17][17];
int n;
int tmp[17][17];
void input(){
    int i,j;
    scanf("%d",&n);
    for(i=1;i<=n;++i) scanf("%s",a[i]+1);
}
void reset(){
    int i,j;
    for(i=1;i<=n;++i)
        for(j=1;j<=n;++j)
            tmp[i][j]=(a[i][j]=='w'?0:1);
}
void flip(int x,int y){
    tmp[x][y]^=1;
    if(x>1) tmp[x-1][y]^=1;
    if(x<n) tmp[x+1][y]^=1;
    if(y>1) tmp[x][y-1]^=1;
    if(y<n) tmp[x][y+1]^=1;
}
int check(int f,int c){
    int i,j,k,tot=0;
    for(i=1;i<=n;++i){
        if(f&(1<<i-1)){
            flip(1,i);
            tot++;
        }
    }
    for(i=1;i<n;++i){
        for(j=1;j<=n;++j){
            if(tmp[i][j]!=c){
                flip(i+1,j);
                tot++;
            }
        }
    }
    for(i=1;i<=n;++i){
        if(tmp[n][i]!=c) tot=inf;
    }
    return tot;
}
int main(){
    int i,j,ans=inf;
    input();
    for(i=0;i<(1<<n);++i){
        reset();
        ans=min(ans,check(i,0));
    }
    for(i=0;i<(1<<n);++i){
        reset();
        ans=min(ans,check(i,1));
    }
    if(ans<inf) printf("%d",ans);
    else printf("Impossible");
    // system("pause");
    return 0;
}
posted @ 2022-03-15 17:27  文艺平衡树  阅读(64)  评论(0编辑  收藏  举报