虽然看了题解,一发AC还是美滋滋的。
枚举一部分情况,根据这些枚举的结果进行推导。
题意是给一个网格,网格有0有1,其中0代表这这个格子的颜色为白,1代表着这个格子的颜色为黑。(每次反转颜色由黑色转为白色或从白色转为黑色)
为了提高牛的智商..(好厉害的农场主),所以农场主让牛玩游戏,牛每次选择一个格子,这个格子会反转(同时其上下左右的格子都反转),对于边界,如最左上角,只反转3个,问牛最少需要选择几个格子可以做到将全部网格变白色,如果有相同个数,输出字典序最小的那种踩格子方案。
刚开始第一次看这个题并不能看懂,过了一遍挑战程序设计竞赛的中级篇重新看以后才明白的。
如果枚举全部可能是2^(r*c)种可能,虽然r,c<=15,但是还是太慢。
可以有这么一个方法,假设第一排踩格子的方法已经确定,从第二排开始我们就能确定踩还是不踩了。
如,如果第r-1行第c列为黑格子,那么显然只有第r行第c列能改变这个格子的颜色(从上到下推),为了让全部格子变白色,那么第r行第c列的格子必定得踩,第r-1行c+1列的格子为黑色,那么通过选择由第r行第c+1列的格子让其变成白色。这样推到第r行第c列的时候,从开头到第r行第c-1列就全为白色了。
那么当推完到最后一个的时候,只保证了前面c-1行全部为白色,那么只需要判断第r行是否全部为白色,如果全部为白色,那么这是一个可行方案。
所以通过枚举第一行的2^c种可能让第一行的情况确定,就可以推着求解了,其中要求字典序最小,二进制枚举的时候,1从右边开始增加就好了。
AC代码
#include <iostream>
#include <stdio.h>
#include <queue>
#include <string.h>
#include <stdlib.h>
#include <set>
#include <algorithm>
#include <map>
using namespace std;
int qq[20][20];
int fan[20][20];
int q1[20][20];
int r,c;
int fun1(int x){
if(x==1)
return 0;
else return 1;
}
int fun2(int r,int c){
int t1=fan[r-1][c]+fan[r-2][c]+fan[r-1][c-1]+fan[r-1][c+1]+qq[r-1][c];
return t1;
}
int fun(int t1){
int i,j;
for(i=2;i<=r;i++)
for(j=1;j<=c;j++)
if(fun2(i,j)%2==1){
fan[i][j]=1; //路麓脳陋脪禄麓脦i,j
t1++;
}else{
fan[i][j]=0;
}
int t2=0;
for(j=1;j<=c;j++)
if(fun2(r+1,j)%2==1)t2=1; //涓洪粦鑹?
if(t2==1)return 0;
else return t1;
}
int main(){
int i,j,k,f1,f2,f3,f4,t1,t2,t3,t4;
//freopen("in.txt","r",stdin);
cin >> r>> c;
memset(qq,0,sizeof(qq));
for(i=1;i<=r;i++){
for(j=1;j<=c;j++){
cin >> qq[i][j];
}
}
t2=1e9+7;
memset(fan,0,sizeof(fan));
memset(q1,0,sizeof(q1));
for(i=0;i<1<<c;i++){
t1=0;
for(j=0;j<c;j++){
if(i>>j&1){ //j涓嶄负0
fan[1][c-j]=1; //寰楃煡绗竴琛岀炕涓庝笉缈?
t1++;
}else{
fan[1][c-j]=0;
}
}
//cout << i << "aaa " <<t1 <<endl;
t3=fun(t1);
//cout << i << " bbb " << t3 << endl;
if(t3&&t3<t2){
t2=t3;
// cout << t2<< endl;
for(k=1;k<=r;k++)
for(j=1;j<=c;j++)
q1[k][j]=fan[k][j];
// cout << "涓€娆? << endl;
}
}
if(t2==1e9+7)
cout <<"IMPOSSIBLE"<<endl;
else{
for(i=1;i<=r;i++){
for(j=1;j<=c;j++)
cout << q1[i][j] <<" ";
cout << endl;
}
}
return 0;
}