hrbust 2163 棋盘取数【最大权独立集合-------最大流Dinic】

棋盘取数
Time Limit: 1000 MSMemory Limit: 32768 K
Total Submit: 58(18 users)Total Accepted: 13(8 users)Rating: Special Judge: No
Description

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

Input

包括多个测试实例,每个测试实例包括一个整数n 和n*n个非负数x(n<=20, 0 <= x <= 1000)。

Output

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

Sample Input

3

258 83 905

874 941 662

733 415 890

Sample Output

3727

Source
2014暑假集训练习赛(8月13日)


思路


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


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

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

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

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


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


4、Ford_Fulkerson 、Edmond _Karp都是不可行算法(数据将两种算法都卡了TLE)。



Ac:


#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
#define INF 0x3f3f3f3f
int head[100000];
struct EdgeNode
{
    int to;
    int w;
    int next;
}e[1000000];
int div[100000];
int a[25][25];
int fx[4]={0,0,1,-1};
int fy[4]={1,-1,0,0};
int n,cont,ss,tt;
void add(int from,int to,int w)
{
    e[cont].to=to;
    e[cont].w=w;
    e[cont].next=head[from];
    head[from]=cont++;
}
void getmap()
{
    ss=n*n+1;
    tt=ss+1;
    cont=0;
    memset(head,-1,sizeof(head));
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if((i+j)%2==0)
            {
                add(ss,(i-1)*n+j,a[i][j]);
                add((i-1)*n+j,ss,0);
            }
            else
            {
                add((i-1)*n+j,tt,a[i][j]);
                add(tt,(i-1)*n+j,0);
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if((i+j)%2==1)continue;
            for(int k=0;k<4;k++)
            {
                int x=i+fx[k];
                int y=j+fy[k];
                if(x>=1&&x<=n&&y>=1&&y<=n)
                {
                    add((i-1)*n+j,(x-1)*n+y,INF);
                    add((x-1)*n+y,(i-1)*n+j,0);
                }
            }
        }
    }
}
int makediv()
{
    memset(div,0,sizeof(div));
    div[ss]=1;
    queue<int >s;
    s.push(ss);
    while(!s.empty())
    {
        int u=s.front();
        if(u==tt)return 1;
        s.pop();
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            int v=e[i].to;
            int w=e[i].w;
            if(w&&div[v]==0)
            {
                div[v]=div[u]+1;
                s.push(v);
            }
        }
    }
    return 0;
}
int Dfs(int u,int maxflow,int tt)
{
    if(u==tt)return maxflow;
    int ret=0;
    for(int i=head[u];i!=-1;i=e[i].next)
    {
        int v=e[i].to;
        int w=e[i].w;
        if(w&&div[v]==div[u]+1)
        {
            int f=Dfs(v,min(maxflow-ret,w),tt);
            e[i].w-=f;
            e[i^1].w+=f;
            ret+=f;
            if(ret==maxflow)return ret;
        }
    }
    return ret;
}
int Dinic()
{
    int ans=0;
    while(makediv()==1)
    {
        ans+=Dfs(ss,INF,tt);
    }
    return ans;
}
int main()
{
    while(~scanf("%d",&n))
    {
        int output=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                scanf("%d",&a[i][j]);
                output+=a[i][j];
            }
        }
        getmap();
        output-=Dinic();
        printf("%d\n",output);
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值