对于每一个数字,或取或不取,记1为取该数,0为不取该数,对于每行的数来说,它的状态就可以用一个二进制的数来描述,对于第一行,若果我们取75,21,我们就可以用二进制的5来描述,即101,因为取的数所在的2个格子不能相邻,所以每一行的二进制数不能有相邻的1,再来看列,相邻的两行不能有相邻的,对于两个二进制,也就是两个数相与(&)为0,这样就可以得到当前的行和上一行的关系,dp[i][j]=dp[i-1][k]+sum(j)
dp[i][j]表示第i行在j状态,dp[i-1][k] 表示第i-1行在k状态,sum(j)表示第i行在状态k下所取数的和,当然k&j==0
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
const int maxn = 18000;
int n;
int map[25][25],dp[2][maxn];
int num[21];
int k,st[maxn]; //k为状态数 st存每行可能的状态
bool ok( int x )
{
int pre = 0;
while( x )
{
if( pre == 1 && x%2 )
return 0;
pre = x%2;
x /= 2;
}
return 1;
}
void init()
{
num[0] = 1;
for( int i = 1; i <= 20; i ++ ) //n位数2进制数最多有num[n]种
{
num[i] = num[i-1] << 1;
}
k = 0;
for( int i = 0; i < num[20]; i ++ )
{
if( ok(i) )
st[k++] = i;
}
}
int main()
{
init();
while( scanf("%d",&n) != EOF )
{
if( n == 0 )
{
printf("0\n");
continue;
}
for( int i = 0; i < n; i ++ )
{
for( int j = 0; j < n; j ++ )
{
scanf("%d",&map[i][j]);
}
}
int Max = -1;
memset( dp,0,sizeof(dp) );
for( int i = 0; st[i] < num[n] && i < k; i ++ ) //便利第一行状态 n位数2进制数最多有num[n]种
{
for( int j = 0; j < n; j ++ )
{
if( st[i] & num[j] )
dp[0][i] += map[0][j];
if( Max < dp[0][i] )
Max = dp[0][i];
}
}
for( int h = 1; h < n; h ++ ) //1~n-1行
{
for( int i = 0; st[i] < num[n] && i < k; i ++ )//枚举第h行状态为st[i]
{
int temp = 0;
for( int j = 0; j < n; j ++ )
{
if( st[i] & num[j] )
temp += map[h][j];
}
for( int j = 0; st[j] < num[n] && j < k; j ++ )//枚举第h-1行状态为st[j]
{
if( !(st[j]&st[i]) && dp[1][i] < dp[0][j]+temp )
{
dp[1][i] = dp[0][j]+temp;
Max = dp[1][i] > Max?dp[1][i]:Max;
}
}
}
for( int i = 0; st[i] < num[n] && i < k; i ++ )
{
dp[0][i] = dp[1][i];
dp[1][i] = 0;
}
}
printf("%d\n",Max);
}
return 0;
}