HDU 1565 方格取数(1)

题目链接: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个格子不能相邻,并且取出的数的和最大。
 

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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值