题目链接: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;
}
这是一篇关于解决矩阵翻转问题的算法解析博客。博主首先分享了使用暴力搜索的方法,但发现会导致超时,然后介绍了正确的解决方案,即通过枚举第一行的翻转情况,结合矩阵的特点优化搜索过程,从而降低时间复杂度。博主提供了正确代码,并在最后给出了测试用例。
2214

被折叠的 条评论
为什么被折叠?



