hdu1565 方格取数(1)

方格取数(1)

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 8656    Accepted Submission(s): 3299


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
 

Author
ailyanlu
 

Source
 

Recommend
8600   |   We have carefully selected several similar problems for you:   1569  1532  3338  1533  1733 

经典的最大点权独立集问题。

  1. 定理:  https://wenku.baidu.com/view/986baf00b52acfc789ebc9a9.html
  2. 最小割=最大流=最小点权覆盖集=sum - 最大点权独立集  


二分图最小点权覆盖

    从x或者y集合中选取一些点,使这些点覆盖所有的边,并且选出来的点的权值尽可能小。

建模:

    原二分图中的边(u,v)替换为容量为INF的有向边(u,v),设立源点s和汇点t,将s和x集合中的点相连,容量为该点的权值;将y中的点同t相连,容量为该点的权值。在新图上求最大流,最大流量即为最小点权覆盖的权值和。

 

二分图最大点权独立集

    在二分图中找到权值和最大的点集,使得它们之间两两没有边。其实它是最小点权覆盖的对偶问题。答案=总权值-最小点权覆盖集
覆盖集。具体证明参考胡波涛的论文。


论文网址:https://wenku.baidu.com/view/986baf00b52acfc789ebc9a9.html


#include <bits/stdc++.h>

using namespace std;
const int MAXN=400+5;
const int inf=1e9+7;
int n;
int s,e;
int tu[25][25];
int dx[4]= {0,0,-1,1};
int dy[4]= {-1,1,0,0};

struct node
{
    int u,v,f;
    int next;
} edge[MAXN*520];

int head[MAXN];
int cnt;
void add(int u,int v,int f)
{
    edge[cnt].u=u;
    edge[cnt].v=v;
    edge[cnt].f=f;
    edge[cnt].next=head[u];
    head[u]=cnt++;
    edge[cnt].u=v;
    edge[cnt].v=u;
    edge[cnt].f=0;
    edge[cnt].next=head[v];
    head[v]=cnt++;
}

int dis[MAXN];
int bfs(int s,int t)
{
    int u, v ;
    memset(dis,-1,sizeof(dis));
    dis[s] = 0 ;
    queue<int>q;
    q.push(s) ;
    while( !q.empty() )
    {
        u = q.front();
        q.pop();
        for(int i = head[u] ; i != -1 ; i = edge[i].next)
        {
            v = edge[i].v ;
            if( dis[v] == -1 && edge[i].f )
            {
                dis[v] = dis[u] + 1 ;
                q.push(v) ;
            }
        }
    }
    if( dis[t] > 0 )
        return 1 ;
    return 0 ;
}
int dfs(int s,int t,int min1)
{
    if( s == t )
        return min1 ;
    int  flow, ans = 0 ;
    for(int i = head[s] ; i != -1 ; i = edge[i].next)
    {
        int v = edge[i].v ;
        if( dis[v] == dis[s] + 1 && edge[i].f && (flow = dfs(v,t,min(min1,edge[i].f) ) ) )
        {
            edge[i].f -= flow ;
            edge[i^1].f += flow ;
            ans += flow ;
            min1 -= flow ;
            if( !min1 )
                break;
        }
    }
    if( ans )
        return ans ;
    dis[s] = -1 ;
    return 0;
}
int getMaxFlow()
{
    int maxFlow=0,flow;
    while(bfs(s,e))
    {
        while((flow=dfs(s,e,inf))>0)
            maxFlow+=flow;
    }
    return maxFlow;
}






int main()
{
    while(~scanf("%d",&n))
    {
        s=0;
        e=n*n+1;
        int sum=0;
        cnt=0;
        memset(head,-1,sizeof(head));
        for(int i = 1; i <= n; ++i)
            for(int j = 1; j <= n; ++j)
            {
                scanf("%d",&tu[i][j]);
                sum += tu[i][j];
                if((i + j)%2 == 0)add(0, (i-1)*n+j, tu[i][j]);
                else add((i-1)*n+j, e, tu[i][j]);
                for(int k=0; k<4; ++k)
                {
                    int nx=i+dx[k];
                    int ny=j+dy[k];
                    if(nx>=1&&nx<=n&&ny>=1&&ny<=n&&(i+j)%2==0)
                        add((i-1)*n+j,(nx-1)*n+ny,inf);
                }
            }
            printf("%d\n",sum-getMaxFlow());
    }
    return 0;
}

又学习了~



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值