给你一个n*n的格子的棋盘,每个格子里面有一个非负数。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。
3 75 15 21 75 15 28 34 70 5
188
中文题意,每个格子只有取不取两个状态,0/1表示,把每一行的压缩成一个数就可以了,要注意的是这里N最大20,也就是2位的二进制数显然是越界的,循环起来时间上也肯定不允许,所以必须要预处理出来K种可行的行(没有相邻1的行)出来,这样dp【】【】数组不会越界,时间上也大大节省。这里dp【i】【j】代表第i行在第j种可行状态下的值,动规过程就是dp【i】【j】=max(dp【i】【j】, dp【i-1】【1...K】+sum(j)),sum(j)表示i行第j种状态求和。
代码如下:
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
int n,cnt;
int mp[22][22];
int vis[50000];
int dp[22][50000];
int init(int x)
{
return x & (x<<1);
}
int main()
{
int N, K, M;
int temp;
while(cin>>N)
{
memset(dp, 0, sizeof(dp));
memset(mp, 0, sizeof(mp));
memset(vis, 0, sizeof(vis));
for(int i=1; i<=N; i++)
{
for(int j=1; j<=N; j++)
{
cin>>mp[i][j];
}
}
M=1<<N;
K=0;
for(int i=0; i<M; i++)
{
if(!(init(i)))
{
vis[++K]=i;
//cout<<"i= "<<i<<endl;
}
}
memset(dp, 0, sizeof(dp));
for(int i=1; i<=N; i++)//第i行
{
for(int j=1; j<=K; j++)//第j种状态
{
temp=0;
for(int k=1; k<=N; k++)//第k列
{
if(vis[j] & (1<<(k-1)))
temp+=mp[i][k]; //求状态j的和
}
dp[i][j]=temp;
for(int l=1; l<=K; l++)//上一行 这里第一行不用单独处理
{
if(!(vis[j] & vis[l]))
{
dp[i][j]=max(dp[i][j], dp[i-1][l]+temp);
//cout<<"********"<<endl;
}
}
}
}
int ans=0;
for(int i=1; i<=K; i++)
{
ans=max(ans, dp[N][i]);
}
cout<<ans<<endl;
}
}