洛谷P1985 [USACO07OPEN] Fliptile S 题解
题目链接:P1985 [USACO07OPEN] Fliptile S
题意:二维的开关问题,一次改变该格和四连通格
注:原题是 $(m,n)$ ,但本人不习惯这种表示,所以就用 $(n,m)$ 替代(即 $n$ 行 $m$ 列)!
首先看到题意马上想到搜索,发现状态数 $2^{nm}$ ,直接起飞
显然我们可以发现对于一个位置,翻转次数至多 $2$ 次,否则是没有意义的
不同于链接中那题,本题的四连通会有一定影响,不可直接确定
如果您没做过那题没有关系 qwq
我们考虑 $(1,1)$ ,如果它要翻转,那么一定会影响到 $(1,2)$ 和 $(2,1)$
这样我们又要考虑它右边的,又要考虑它下面的,很麻烦
于是提出设想,如果第一行已经确定了,那是不是可以推出第二行?
答案是可以的
那么我们就可以枚举第一行的翻转状态,然后再逐行推结果
时间复杂度 $O(nm2^m)$
代码如下
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define R register
#define MAXN (int)(25)
#define INF 0x3f3f3f3f3f3f3f3f
int n,m;
template<typename T>inline void read(R T &k)
{
R char ch=getchar(); R T x=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
k=x*f;
}
int a[MAXN][MAXN]; // 原数组
int f[MAXN][MAXN]; // 表示是否翻转该格子及其四连通格
int dx[]={1,-1,0,0,0};
int dy[]={0,0,1,-1,0};
int ans[MAXN][MAXN],mn=INF; // ans[][]记录答案,mn记录最小操作数
int get(R int x,R int y) // 获取格子的状态
{
R int res=a[x][y];
for(R int i=0; i<5; i++) // 自己也要算上 qwq
{
R int tx=x+dx[i];
R int ty=y+dy[i];
if(tx>=1&&tx<=n&&ty>=1&&ty<=m)
res+=f[tx][ty];
}
return res&1; // 显然
}
int proc()
{
R int res=0;
for(R int i=2; i<=n; i++)
for(R int j=1; j<=m; j++)
if(get(i-1,j))f[i][j]=1;
for(R int i=1; i<=n; i++)
for(R int j=1; j<=m; j++)
if(get(i,j))return -1145141919810; // 如果操作完还有没搞定的格子,无解,返回负无穷 qwq
for(R int i=1; i<=n; i++)
for(R int j=1; j<=m; j++)
res+=f[i][j]; // 统计答案
return res;
}
signed main()
{
read(n);read(m);
for(R int i=1; i<=n; i++)
for(R int j=1; j<=m; j++)
read(a[i][j]);
for(R int i=0; i<(1<<m); i++) // 枚举第一行的状态
{
memset(f,0,sizeof(f)); // 别忘了清空
for(R int j=0; j<m; j++)
f[1][j+1]=(i>>j)&1;
R int res=proc();
if(mn>res&&res>=0){mn=res;memcpy(ans,f,sizeof(f));}
}
if(mn==INF)return puts("IMPOSSIBLE"),0;
for(R int i=1; i<=n; i++)
for(R int j=1; j<=m; j++)
printf("%lld%c",ans[i][j]," \n"[j==m]);
return 0;
}