4.翻转(简单搜索 枚举)

翻转

题目链接

题目

给定一个 \(M×N\)\(01\) 矩阵。
你需要选择其中一些元素,并对选择的元素进行翻转操作。
翻转操作是指将所选元素以及与其上下左右相邻的元素(如果有)进行翻转(0 变 1,1 变 0)。
我们希望最终矩阵变为一个全 \(0\) 矩阵,并且选择进行翻转操作的元素数量尽可能少。
输出最佳翻转方案。

输入格式

第一行包含整数 \(M,N\)。接下来 \(M\) 行,每行包含 \(N\) 个整数,每个整数为 \(0\)\(1\)

输出格式

\(M\) 行,每行包含 \(N\) 个整数,其中第 \(i\) 行第 \(j\) 列的整数,表示第 \(i\) 行第 \(j\) 列元素的翻转次数。
如果翻转操作次数最少的操作方案不唯一,则输出字典序最小的方案。

如果不存在合理方案,则输出 IMPOSSIBLE

数据范围

\(1≤M,N≤15\)

输入样例:

4 4
1 0 0 1
0 1 1 0
0 1 1 0
1 0 0 1

输出样例:

0 0 0 0
1 0 0 1
1 0 0 1
0 0 0 0

思路

枚举第一行可能的情况,尝试翻转下一行,使当前行为 \(0\) ,翻转时统计最小反转次数,同时更新要反转的方案

代码

#include<bits/stdc++.h>
using namespace std;
const int N=20;
int g[N][N],backup[N][N];
int cnt,ans[N][N],num[N][N];
int dx[]={-1,0,1,0,0},dy[]={0,1,0,-1,0};
int n,m;

void turn(int x,int y)
{
	for(int i=0;i<5;i++)
	{
		int a=x+dx[i],b=y+dy[i];
		if(a<0||a>=n||b<0||b>=m)continue;
		g[a][b]^=1;
		
	}
}

int main()
{
	cin>>n>>m;
	
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++)
			cin>>backup[i][j];
	
	
	int res=1e9;
	for(int op=0;op<1<<15;op++)
	{
	    int cnt=0;
		memcpy(g,backup,sizeof g);
		memset(num,0,sizeof num);
		for(int i=0;i<n;i++)
			if(op>>i&1)
			{
				turn(0,i);
				num[0][i]++;
				cnt++;
			}
		
		for(int i=0;i<n-1;i++)
		{
			for(int j=0;j<m;j++)
			{
				if(g[i][j]==1)
				{
					turn(i+1,j);
					num[i+1][j]++;
					cnt++;
				}
			}
		}
		
		bool has_one=false;
		for(int i=0;i<m;i++)
			if(g[n-1][i]==1){
				has_one=true;
				break;
			}
		
		if(!has_one)
		{
		    if(res>cnt)
		    {
		        res=cnt;
		        memcpy(ans,num,sizeof ans);
		    }
		}
		
	}
	
	if(res==1e9)puts("IMPOSSIBLE");
	else 
	{
	    for(int i=0;i<n;i++)
	        for(int j=0;j<m;j++)
	            cout<<ans[i][j]<<" \n"[j==m-1];
	}
	
	return 0;
	
}
posted @ 2023-05-01 23:55  风雨zzm  阅读(14)  评论(0编辑  收藏  举报