洛谷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;
}