题意概要:给出一个0,1矩阵,按压一下就会该点的数^1(也就是会翻转)求所有翻成0时最小的翻转次数,如果翻转次数相同,输出最小字典序。
题解:枚举第一行,因为下面一行只受到上一行的影响,也就是上一行的1只能通过下一行来翻成0,枚举时从小到大枚举;
代码
#include<string>
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
#define ll long long
#define up(i,a,n) for(int i=a;i<=n;i++)
int mp[20][20];
int mp1[20][20];
int chose[20][20];
int press[20][20];
int n,m;
int move1[5][2]={0,0,1,0,-1,0,0,1,0,-1};\\翻转的四个方向,因为我从1开始记录,旁边不影响,所以不考虑边界
bool judge();
int solve()
{
int ans=0;
up(i,1,m)
{
if(press[1][i]==1)//枚举的第一行
{
up(j,0,4)
{
mp[1+move1[j][0]][i+move1[j][1]]^=1;
ans++;
}
}
}
up(i,2,n)
{
up(j,1,m)
{
if(mp[i-1][j]==1)//上一行的1只能由下一行解决
{ up(k,0,4)mp[i+move1[k][0]][j+move1[k][1]]^=1;
press[i][j]=1;
ans++;
}
}
}
if(judge())
{
return ans;
}
return -1;
}
bool judge()
{
up(i,1,m)
{
if(mp[n][i]==1)return false;
}
return true;
}
int main()
{
scanf("%d %d",&n,&m);
up(i,1,n)
up(j,1,m)
scanf("%d",&mp1[i][j]);
int Min=99999999;
int goal[20][20];
for(int i=0;i<(1<<n);i++)
{
memset(chose,0,sizeof(chose));
memcpy(mp,mp1,sizeof(mp1));
memset(press,0,sizeof(press));
up(j,1,n){
chose[1][j]=(i>>(j-1))&1;//这里枚举第一行的翻转
press[1][j]=chose[1][j];
}
int MMin=solve();
if(MMin==-1)
{
continue;
}
if(MMin<Min)
{
memcpy(goal,press,sizeof(press));//交换答案
Min=MMin;
}
}
if(Min==99999999)
{
printf("IMPOSSIBLE\n");//没有办法翻转
}
else
{
up(i,1,n)
{
up(j,1,m)
{
printf("%d ",goal[i][j]);//输出答案
}
printf("\n");
}
}
}