hdu 1569 方格取数(2)【最大权独立集合-------最大流Edmond_Karp】

方格取数(2)

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 6000    Accepted Submission(s): 1908


Problem Description
给你一个m*n的格子的棋盘,每个格子里面有一个非负数。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的数的和最大。
 

Input
包括多个测试实例,每个测试实例包括2整数m,n和m*n个非负数(m<=50,n<=50)
 

Output
对于每个测试实例,输出可能取得的最大的和
 

Sample Input
  
  
3 3 75 15 21 75 15 28 34 70 5
 

Sample Output
  
  
188

思路


1、首先,这个题是一个二分图模型。并且我们知道最大权独立集=总权-最小割=总权-最大流。


2、那么我们就在以上基础建图。

①我们将点分成两个集合,i+j为偶数的点我们规定为一个集合,那么i+j为奇数的点就是另一个集合。

②源点S和i+j为偶数的点都进行建边,其容量为a【i】【j】,i+j为奇数的点和汇点t都进行建边,其容量也是a【i】【j】;

③将i+j为偶数的点和周围四个点进行建边,其容量为无穷大。


3、求一遍最大流,其解为:总权-最大流、


Ac代码:


#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<queue>
#include<string.h>
using namespace std;
int head[2550];
struct node
{
    int from,to,w,next;
}e[2550*2550];
int vis[2550];
int pre[255000];
int a[55][55];
int fx[4]={0,0,1,-1};
int fy[4]={1,-1,0,0};
int n,m,s,t,sum,cont;
void add(int from,int to,int w)
{
    e[cont].from=from;
    e[cont].to=to;
    e[cont].w=w;
    e[cont].next=head[from];
    head[from]=cont++;
    e[cont].from=to;
    e[cont].to=from;
    e[cont].w=0;
    e[cont].next=head[to];
    head[to]=cont++;
}
int Ek_Bfs(int start,int end)
{
    memset(vis,0,sizeof(vis));
    memset(pre,-1,sizeof(pre));
    vis[start]=1;
    queue<int >s;
    s.push(start);
    while(!s.empty())
    {
        int u=s.front();
        if(u==end)return 1;
        s.pop();
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            int v=e[i].to;
            if(vis[v]==0&&e[i].w>0)
            {
                vis[v]=1;
                pre[v]=i;
                s.push(v);
            }
        }
    }
    return 0;
}
void Edmond_Karp()
{
    int maxflow=0;
    int minn;
    while(Ek_Bfs(s,t))
    {
        minn=0x3f3f3f3f;
        int u=t;
        while(u!=s)
        {
            minn=min(e[pre[u]].w,minn);
            u=e[pre[u]].from;
        }
        u=t;
        while(pre[u]!=-1)
        {
            e[pre[u]].w-=minn;
            e[pre[u]^1].w+=minn;
            u=e[pre[u]].from;
        }
        maxflow+=minn;
    }
    printf("%d\n",sum-maxflow);
}
void getmap()
{
    cont=0;
    memset(head,-1,sizeof(head));
    s=n*m+1;
    t=n*m+2;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if((i+j)%2==0)
            {
                add(s,(i-1)*m+j,a[i][j]);
            }
            else
            {
                add((i-1)*m+j,t,a[i][j]);
            }

        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if((i+j)%2==0)
            {
                for(int k=0;k<4;k++)
                {
                    int tmpx=i+fx[k];
                    int tmpy=j+fy[k];
                    if(tmpx>=1&&tmpx<=n&&tmpy>=1&&tmpy<=m)
                    {
                        add((i-1)*m+j,(tmpx-1)*m+tmpy,0x3f3f3f3f);
                    }
                }
            }
        }
    }
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        sum=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                scanf("%d",&a[i][j]);
                sum+=a[i][j];
            }
        }
        getmap();
        Edmond_Karp();
    }
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值