题目大意:在 n * n 每格带权棋盘上取若干互不四相邻格,求最大权值和。
轮廓线动态规划。逐格转移,记状态 f[i, j, k] 为处理至第 (i, j) 格(设 0 ≤ i < n,0 ≤ j < n),前 n 格的取舍状态为 k 的方案数,记 0 为未取,1 为已取,利用二进制状态压缩,则 k 二进制下从低位向高位第 l 位表示当前格前第 l 格是否取的状态,可知 k 最多只需记 n 位即可。对于当前状态 k,转移分为以下三种情况:
当前列不为最左列(即 j > 0),此时若格 (i, j - 1) 未取(即 k and 1 = 0),且格 (i - 1, j) 未取(即 k and 2n−1 = 0),则可以取格 (i, j),即状态 f[i, j - 1, k] 可以转移到状态 f[i, j, k * 2 xor 1]。
当前列为最左列(即 j = 0),此时若格 (i - 1, j) 未取(即 k and 2n−1 = 0),则可以取当前格 (i, j),即状态 f[i - 1, n - 1, k] 可以转移到状态 f[i, j, k * 2 xor 1]。
可以不取当前格 (i, j),即状态 f[i, j - 1, k] (或状态 f[i - 1, n - 1, k]) 可以转移到状态 f[i, j, k * 2 and ( 2n−1 )]。
设初始状态 f[-1, n - 1, 0] = 0,f[-1, n - 1, 1 .. 2n−1 ] = -1(不合法状态),转移相当于维护最大值,则状态 f[n - 1, n - 1, 0 .. 2n−1 ] 中的最大值即为答案。可以采用滚动数组优化,降低空间复杂度和编程复杂度。
代码如下:
#include<stdio.h>
#define max(a,b) ((a)>(b)?(a):(b))
int f[2][1048580];
int main()
{
int cur,n,val;
while(scanf("%d",&n)!=EOF)
{
for(int i=0;i<1<<n;i++)
f[0][i]=f[1][i]=-1;
cur=f[0][0]=val=0;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
{
scanf("%d",&val);
for(int k=0;k<1<<n-1;k++,k+=j>0)
if(f[cur][k]>-1&&f[cur^1][k<<1^1]<f[cur][k]+val)
f[cur^1][k<<1^1]=f[cur][k]+val;
for(int k=0;k<1<<n-1;k++)
f[cur^1][k<<1]=max(f[cur^1][k<<1],f[cur][k]),f[cur][k]=-1,
f[cur^1][k<<1]=max(f[cur^1][k<<1],f[cur][k^1<<n-1]),f[cur][k^1<<n-1]=-1;
cur^=1;
}
for(int i=0;i<1<<n;i++)
if(f[cur][i]>val)
val=f[cur][i];
printf("%d\n",val);
}
return 0;
}