题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1565
方格取数(1)
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 5932 Accepted Submission(s): 2253
Problem Description
给你一个n*n的格子的棋盘,每个格子里面有一个非负数。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。
Input
包括多个测试实例,每个测试实例包括一个整数n 和n*n个非负数(n<=20)
Output
对于每个测试实例,输出可能取得的最大的和
Sample Input
3 75 15 21 75 15 28 34 70 5
Sample Output
188
也可以用状压DP搞
显然网络流略胜一筹
这道题实际上是求最大点权独立集,可以将矩阵分成两类,如何分类?比如某个元素是0,则让其周围元素都为1
最大点权独立集=总权-最小割=总权-最大流,具体证明请看二分图匹配的相关书籍
下面是用网络流dinic算法AC的代码
#include <stdio.h>
#include <string.h>
#define maxn 500
#define inf 1<<30
int n, sink, source;
int vis[maxn], c[maxn][maxn];
int dfs(int x, int low)
{
if (x == sink)return low;
if (vis[x])return 0;
vis[x]=1;
for (int i = source,flow;i<=sink;i++)
{
if (c[x][i] && (flow = dfs(i, low<c[x][i]?low:c[x][i])))
{
c[x][i]-=flow;
c[i][x] += flow;
return flow;
}
}
return 0;
}
int maxflow()
{
int ans = 0, flow;
memset(vis, 0, sizeof(vis));
while (flow = dfs(source, inf))
{
memset(vis,0,sizeof(vis));
ans += flow;
}
return ans;
}
int main()
{
int i, m, f[maxn],total;
while (scanf("%d", & n)!=EOF)
{
source = total = 0;
sink = n * n + 1;
memset(f,0,sizeof(f));
memset(c,0,sizeof(c));
for (i = 1; i <= n * n; i++)
{
scanf("%d", & m);
total += m;
if (i <= n) f[i] = (f[i - 1] + 1) % 2;
else f[i] = (f[i-n] + 1) % 2;
if (f[i] == 1)
{
if (i % n != 0) c[i][i+1] = inf;
if (i % n != 1) c[i][i-1]= inf;
if (i <= n * (n - 1)) c[i][i+n] = inf;
if (i > n) c[i][i-n] = inf;
c[source][i] = m;
}
else
c[i][sink] = m;
}
printf("%d\n",total - maxflow());
}
return 0;
}
那状压DP就更简单了。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<sstream>
#include<vector>
#include<map>
#include<stack>
#include<list>
#include<set>
#include<queue>
#define LL long long
#define mod 100000000
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1 | 1
using namespace std;
const int maxn=1<<21,inf=1<<29;
int dp[2][maxn];
int top,s[maxn],n;
int Map[50][50];
bool is(int x)
{
if(x&(x<<1)) return 0;
return 1;
}
void init(int m)
{
top=0;
for(int i=0;i<(1<<m);i++)
if(is(i)) s[top++]=i;
}
int Count(int i,int r)
{
int sum=0;
// printf("jjjjjjjjjjjjj\n");
for(int j=0;j<n;j++)
if(s[i]&(1<<j)) sum+=Map[r][j];
return sum;
}
int main()
{
init(20);
while(~scanf("%d",&n))
{
memset(dp,0,sizeof(dp));
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
scanf("%d",&Map[i][j]);
for(int i=0;i<top;i++)
{
if(i>(1<<n)) break;
dp[0][s[i]]=Count(i,0);
}
// printf("yes\n");
for(int i=1;i<n;i++)
{
int p=(i&1);
for(int j=0;j<top;j++)
{
if(s[j]>(1<<n)) break;
int sum=Count(j,i),Max=0;
for(int k=0;k<top;k++)
{
if(s[k]>(1<<n)) break;
if(!(s[j]&s[k])) Max=max(Max,dp[1-p][s[k]]);
}
dp[p][s[j]]=Max+sum;
}
}
int Max=0;
for(int i=0;i<2;i++)
for(int j=0;j<top;j++)
Max=max(Max,dp[i][s[j]]);
printf("%d\n",Max);
}
return 0;
}