题目传送门:https://loj.ac/problem/6301
题目分析:比赛的时候乱写了个 O(n2m3m) O ( n 2 m 3 m ) 的状压DP,结果只拿了14pts。当时我还在想怎么才能拿第一个点的那10pts,要不要再写一个另外的算法。后来发现由于 n<m n < m ,只需要交换 n,m n , m ,稍微处理一下就好了QAQ。赛后30min发现自己还能写 O(n3m) O ( n 3 m ) 的方法,于是补了一发,然而卡不过 n=15 n = 15 的点……
其实我之前写的状压DP都是按行转移的。插头DP可以单格子转移但因为太难写我就弃了,这题比较像状压才去写了一下(依然比较难写)。这题要按行转移就必须枚举子集,时间开销太高。加上题目中只令一个格子变为障碍物这样的特殊条件,还是一个一个格子转移更优。如果在单格子(i,j)处转移的话,状压的就是(i,j)左上方的一条轮廓线,这条轮廓线同样只需要用0/1记录有没有下插头即可。而(i,j)处有两种插头(左和下),所以要加多一位,总状态数变为 2m+1 2 m + 1 。转移的时候通过分类讨论和一些位运算,即可做到 O(nm2m) O ( n m 2 m ) 。
按地图正反都做一遍这个DP,然后枚举答案,将两边的状态对应起来,同样是这个时间复杂度。注意加一些底层优化。总之本机测试不开O2优化最慢的点要跑3.8s,交上LOJ开了O2后只要900多msOwO。
代码奇长系列
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=22;
const int maxs=(1<<18)+100;
const int M=1000000007;
int f[maxn][maxn][maxs];
int g[maxn][maxn][maxs];
int map[maxn][maxn];
int n,m;
int Get(int x)
{
return (1<<(x-1));
}
int Mod(int x)
{
if (x>=M) return x-M;
return x;
}
int main()
{
freopen("C.in","r",stdin);
freopen("C.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1; i<=n; i++)
for (int j=1; j<=m; j++) scanf("%d",&map[i][j]);
int ms=(1<<(m+1));
f[1][1][0]=1;
if (!map[1][1]) f[1][1][1]=1,f[1][1][2]=1;
for (int i=1; i<=n; i++)
for (int j=1; j<=m; j++) if ( i<n || j<m )
for (int s=0; s<ms; s++) if (f[i][j][s])
{
int x=(s&Get(j+1));
int y=(s&Get(j+2));
if ( (x&&y) || ( (x||y) && map[i][j+1] ) ) continue;
if (j<m)
if (map[i][j+1])
{
int &v=f[i][j+1][s];
v=Mod(v+f[i][j][s]);
}
else
{
if (x)
{
int &v=f[i][j+1][s^Get(j+1)];
v=Mod(v+f[i][j][s]);
}
if (y)
{
int &v=f[i][j+1][s^Get(j+2)];
v=Mod(v+f[i][j][s]);
}
if ( !x && !y )
{
int num=f[i][j][s];
int &p=f[i][j+1][s];
p=Mod(p+num);
int &q=f[i][j+1][s|Get(j+1)];
q=Mod(q+num);
int &r=f[i][j+1][s|Get(j+2)];
r=Mod(r+num);
}
}
else
{
if (x) continue;
y=(s&1);
if ( y && map[i+1][1] ) continue;
if (map[i+1][1])
{
int &v=f[i+1][1][s<<1];
v=Mod(v+f[i][j][s]);
}
else
if (y)
{
int &v=f[i+1][1][(s^1)<<1];
v=Mod(v+f[i][j][s]);
}
else
{
int num=f[i][j][s],t=(s<<1);
int &p=f[i+1][1][t];
p=Mod(p+num);
int &q=f[i+1][1][t|1];
q=Mod(q+num);
int &r=f[i+1][1][t|2];
r=Mod(r+num);
}
}
}
//printf("%d\n",f[n][m][0]);
g[n][m][0]=1;
if (!map[n][m]) g[n][m][Get(m+1)]=1,g[n][m][Get(m)]=1;
for (int i=n; i>=1; i--)
for (int j=m; j>=1; j--) if ( i>1 || j>1 )
for (int s=0; s<ms; s++) if (g[i][j][s])
{
if (j>1)
{
int x=(s&Get(j));
int y=(s&Get(j-1));
if ( (x&&y) || ( (x||y) && map[i][j-1] ) ) continue;
if (map[i][j-1])
{
int &v=g[i][j-1][s];
v=Mod(v+g[i][j][s]);
}
else
{
if (x)
{
int &v=g[i][j-1][s^Get(j)];
v=Mod(v+g[i][j][s]);
}
if (y)
{
int &v=g[i][j-1][s^Get(j-1)];
v=Mod(v+g[i][j][s]);
}
if ( !x && !y )
{
int num=g[i][j][s];
int &p=g[i][j-1][s];
p=Mod(p+num);
int &q=g[i][j-1][s|Get(j-1)];
q=Mod(q+num);
int &r=g[i][j-1][s|Get(j)];
r=Mod(r+num);
}
}
}
else
{
if (s&1) continue;
int y=(s&Get(m+1));
if ( y && map[i-1][m] ) continue;
if (map[i-1][m])
{
int &v=g[i-1][m][s>>1];
v=Mod(v+g[i][j][s]);
}
else
if (y)
{
int &v=g[i-1][m][(s^Get(m+1))>>1];
v=Mod(v+g[i][j][s]);
}
else
{
int num=g[i][j][s],t=(s>>1);
int &p=g[i-1][m][t];
p=Mod(p+num);
int &q=g[i-1][m][t|Get(m)];
q=Mod(q+num);
int &r=g[i-1][m][t|Get(m+1)];
r=Mod(r+num);
}
}
}
//printf("%d\n",g[1][1][0]);
f[0][m][0]=1;
for (int i=0; i<n; i++)
for (int s=0; s<ms; s++) if (!(s&Get(m+1)))
f[i+1][0][s<<1]=f[i][m][s];
g[n+1][1][0]=1;
for (int i=n+1; i>1; i--)
for (int s=0; s<ms; s++) if (!(s&1))
g[i-1][m+1][s>>1]=g[i][1][s];
for (int i=1; i<=n; i++)
{
for (int j=1; j<=m; j++)
if (map[i][j]) printf("0 ");
else
{
int ans=0;
for (int s=0; s<ms; s++)
if ( !(s&Get(j)) && !(s&Get(j+1)) )
ans=Mod(ans+ (long long)f[i][j-1][s]*g[i][j+1][s]%M );
printf("%d ",ans);
}
printf("\n");
}
return 0;
}