(POJ - 3279)Fliptile(搜索)

这是一篇关于解决矩阵翻转问题的算法解析博客。博主首先分享了使用暴力搜索的方法,但发现会导致超时,然后介绍了正确的解决方案,即通过枚举第一行的翻转情况,结合矩阵的特点优化搜索过程,从而降低时间复杂度。博主提供了正确代码,并在最后给出了测试用例。
摘要由CSDN通过智能技术生成

题目链接:3279 -- Fliptile

题意:给你一个01矩阵,矩阵大小为M x N。(1 <= M , N <= 15)
每次操作选择一个格子,使得该格子与上下左右四个格子的值翻转。
至少多少次操作可以使得矩阵中所有的值变为0?
请输出翻转方案,若没有方案,输出"IMPOSSIBLE” 。

这道题目一开始我是直接暴力搜索,这个比较简单,然后提交就返回了超时,一会我会附上暴力搜索代码

正确思路:首先我们需要知道的一点是,每个格子最多只能被反转一次,若反转两次则相当于没有反转,我们枚举第一行格子可能的反转情况,之后反转情况就固定了(因为上一行的1需要通过利用下一行对应的格子反转来进行置0),因为如果第一行的反转情况已经固定,之后我们是对第二行及其之后的格子进行反转,倘若第一行第5个格子还是1,那么我们就只能通过反转第二行第5个格子来使他变为0了,依次类推,直到反转完最后一行,因为反转最后一行实质上是为了修改倒数第二行,所以我们最后需要判断一下最后一行格子是不是全0来判断当前枚举方案是否合法,我们在过程中记录反转次数和反转方案,依次更新反转次数和反转方案即可。

下面附上暴力搜索代码和正确代码:

暴力搜索代码(超时代码):

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
using namespace std;
const int N=20;
int s[N][N],dx[5]={0,0,0,1,-1},dy[5]={0,1,-1,0,0};
int m,n,ans;
int tvis[N][N];//标记哪个位置被反转
int vis[N][N];//标记最少反转次数方案 
int reverse(int x,int y)
{
	for(int i=0;i<5;i++)
	{
		int nx=x+dx[i],ny=y+dy[i];
		if(nx>=1&&nx<=m&&ny>=1&&ny<=n)
			s[nx][ny]^=1;
	}
}
bool check()
{
	for(int i=1;i<=m;i++)
	for(int j=1;j<=n;j++)
		if(s[i][j]) return false;
	return true;
}
void dfs(int x,int y,int cnt)
{
	if(check()&&cnt<ans)
	{
		memcpy(vis,tvis,sizeof tvis);
		ans=cnt;
		return ;
	}
	int t=(x-1)*n+y;
	if(t==n*m) return ;
	dfs(t/n+1,t%n+1,cnt);
	reverse(x,y);
	tvis[x][y]^=1;
	dfs(t/n+1,t%n+1,cnt+1);
	reverse(x,y);
	tvis[x][y]^=1;
}
int main()
{
	cin>>m>>n;
	for(int i=1;i<=m;i++)
	for(int j=1;j<=n;j++)
		scanf("%d",&s[i][j]);
	ans=0x3f3f3f3f;
	dfs(1,1,0);
	if(ans==0x3f3f3f3f) puts("IMPOSSIBLE");
	else
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<=n;j++)
			printf("%d ",vis[i][j]);
		puts("");
	}
	return 0;
} 

下面是正确代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
using namespace std;
const int N=20;
int s[N][N],ts[N][N],dx[5]={0,0,0,1,-1},dy[5]={0,1,-1,0,0};
int m,n,ans;
int tvis[N][N];//标记哪个位置被反转
int vis[N][N];//标记最少反转次数方案
int reverse(int x,int y)
{
	for(int i=0;i<5;i++)
	{
		int nx=x+dx[i],ny=y+dy[i];
		if(nx>=1&&nx<=m&&ny>=1&&ny<=n)
			s[nx][ny]^=1;
	}
}
bool check()
{
	for(int i=1;i<=n;i++)
		if(s[m][i]) return false;
	return true;
}
int main()
{
	cin>>m>>n;
	for(int i=1;i<=m;i++)
	for(int j=1;j<=n;j++)
		scanf("%d",&s[i][j]);
	memcpy(ts,s,sizeof s);
	ans=0x3f3f3f3f;
	for(int i=0;i<1<<n;i++)
	{
		memcpy(s,ts,sizeof ts);
		memset(tvis,0,sizeof tvis);
		int cnt=0;
		for(int j=0;j<n;j++)
		{
			if(i>>j&1)
			{
				cnt++;
				tvis[1][j+1]=1;
				reverse(1,j+1);
			}
		}
		for(int j=1;j<=m-1;j++)
		for(int k=1;k<=n;k++)
		{
			if(s[j][k])
			{
				cnt++;
				tvis[j+1][k]=1;
				reverse(j+1,k);
			}
		}
		if(check()&&(cnt<ans))
		{
			ans=cnt;
			memcpy(vis,tvis,sizeof tvis);
		}
	}
	if(ans==0x3f3f3f3f) puts("IMPOSSIBLE");
	else
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<=n;j++)
			printf("%d ",vis[i][j]);
		puts("");
	}
	return 0;
} 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值