题目:Fliptile
题意:
给定一个M*N矩阵,有些是黑色(1表示)否则白色(0表示),每翻转一个(i,j),会使得它和它周围4个格变为另一个颜色,要求翻转最少的点,使得变为全白色的矩阵,输出这个标记了翻转点的矩阵,如果有多个最优解,输出逆字典序最小的那个矩阵,若没有解,输出IMPOSSIBLE。
题解:
解法一:(对第一行的踩法进行DFS搜索)

#include <iostream> #include <cstring> #include <queue> #include <cmath> using namespace std; #define ll long long const int inf=0x3f3f3f3f; const ll inff=0x3f3f3f3f3f3f3f3f; int m,n; int a[20][20];//每一遍DFS后的初始状态(即第一行踩过后进入solve前的状态) int ans[20][20];//记录DFS下第一行的踩法,这里二维数组多余了... int pace=inf; int ann[20][20];//最终的踩法 void cai(int j)//踩第一行第j个 { ans[1][j]=(ans[1][j]==1? 0:1); if(j>1) a[1][j-1]=(a[1][j-1]==1? 0:1); a[1][j]=(a[1][j]==1? 0:1); if(j<n) a[1][j+1]=(a[1][j+1]==1? 0:1); if(m>=2) a[2][j]=(a[2][j]==1? 0:1); } void solve(int pa) { int a1[20][20]; int ans1[20][20];//记录此例下的结果 for(int i=1;i<20;i++) for(int j=1;j<20;j++) ans1[i][j]=ans[i][j],a1[i][j]=a[i][j]; for(int i=1;i<m;i++) { for(int j=1;j<=n;j++) { if(a1[i][j]){ ans1[i+1][j]=1; pa++; if(j>1) a1[i+1][j-1]=(a1[i+1][j-1]==1? 0:1); a1[i+1][j]=(a1[i+1][j]==1? 0:1); if(j<n) a1[i+1][j+1]=(a1[i+1][j+1]==1? 0:1); a1[i][j]=(a1[i][j]==1? 0:1); if(i+2<=m) a1[i+2][j]=(a1[i+2][j]==1? 0:1); } } } for(int j=1;j<=n;j++)//最后一行判断,若没有1,则成功 { if(a1[m][j]) { return ; } } if(pa<pace){//记录最小踩法 pace=pa; for(int i=1;i<20;i++) for(int j=1;j<20;j++) ann[i][j]=ans1[i][j]; } } void DFS(int x,int pa)//第一行第x个踩还是不踩 {//pa为此次DFS决定踩不踩之前,第一行已经踩过的步数 cai(x);//踩 solve(pa+1); if(x<n) DFS(x+1,pa+1); cai(x);//不踩(再踩一次相当于不踩) solve(pa); if(x<n) DFS(x+1,pa); } void print() { if(pace==inf){ cout<<"IMPOSSIBLE"<<endl; return ; } //cout<<pace<<endl; for(int i=1;i<=m;i++) { for(int j=1;j<=n;j++) { cout<<ann[i][j]; if(j!=n) cout<<" "; } cout<<endl; } } int main() { cin>>m>>n; for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) cin>>a[i][j]; DFS(1,0); print(); return 0; }
参考:Fliptile POJ3279 二进制压缩枚举 解题报告
只要第一行的方案确定,后面的踩发就能确定,所以状压枚举第一行的方案
代码:

/***********************************************/ int ans[30][30]; int a[34][34]; int b[33][33]; int m,n; int ANS=inf; ll daan=-1; int fanzhuan1(ll Y) { mem0(ans); int pp=0; for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) a[i][j]=b[i][j]; for(int i=0;i<n;i++) { if(Y&(1ll<<i)) //翻转第一行第n-i个 { pp++; ans[1][n-i]=1; a[1][n-i]=a[1][n-i]?0:1; if(i>0) a[1][n-i+1]=a[1][n-i+1]?0:1; if(i<n-1) a[1][n-i-1]=a[1][n-i-1]?0:1; if(n>1) a[2][n-i]=a[2][n-i]?0:1; } } return pp; } void solve(int pp,ll p1) { for(int i=1;i<m;i++) { for(int j=n;j>=1;j--) { if(a[i][j]) { ans[i+1][j]=1; a[i][j]=0; a[i+1][j]=a[i+1][j]?0:1; if(j>1) a[i+1][j-1]=a[i+1][j-1]?0:1; if(j<n) a[i+1][j+1]=a[i+1][j+1]?0:1; if(i+2<=m) a[i+2][j]=a[i+2][j]?0:1; } } } for(int j=1;j<=n;j++) { if(a[m][j]){ //cout<<"IMPOSSIBLE"<<endl; return ; } } ///取最优 int sum=pp; for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) { if(ans[i][j]) sum++; } if(sum<ANS) { ANS=sum; daan=p1; } } int main() { std::ios::sync_with_stdio(false); std::cin.tie(0); cin>>m>>n; for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) cin>>b[i][j]; int Y=(1<<n)-1; for(ll i=0;i<=Y;i++) { int t=fanzhuan1(i); //cout<<t<<endl; solve(t,i); } if(daan==-1) cout<<"IMPOSSIBLE"<<endl; else { //cout<<daan<<endl; int t=fanzhuan1(daan); solve(t,daan); for(int i=1;i<=m;i++) { for(int j=1;j<n;j++) cout<<ans[i][j]<<" "; cout<<ans[i][n]<<endl; } } return 0; }