hdu 3061 Battle 题解【网络流】【最小割】【最大权闭合子图】

作者: wjyyy 分类: 最大权闭合子图,最小割,网络流,解题报告 发布时间: 2019-01-22 19:26

点击量:30

最大权闭合子图的理解。

Problem Description

由于小白同学近期习武十分刻苦,很快被晋升为天策军的统帅。而他上任的第一天,就面对了一场极其困难的战斗:

据侦查兵回报,前方共有N座城池,考虑到地势原因,最终得到一个结论:攻占某些城池之前必须攻占另外一些城池。

事实上,可以把地图看做是一张拓扑图,而攻占某个城池,就意味着必须先攻占它的所有前驱结点。

小白还做了一份调查,得到了攻占每个城池会对他的兵力产生多少消耗(当然也可能会得到增长,因为每攻占一个城池,便可以整顿军队,扩充兵力,天策军的兵力十分庞大,如果不考虑收益,他们可以攻取所有的城池)。

现在请你帮小白统帅做一份战斗计划,挑选攻打哪些城市,使得天策军在战斗过后军容最为壮大。

Input

首先输入一个\(N\),代表有\(N\)个城池(\(1\le n\le 500\))

接着输入一个\(M\),代表城池和城池之间的拓扑关系数。

接着输入\(N\)个数字,代表从\(1\)到\(N\)编号城池的战斗消耗(负数代表将要消耗天策军兵力,正数表示天策军可以获得相应的战斗收益)

最后\(M\)行,每行\(2\)个数字\(a\),\(b\),代表相应城池的编号。

表示攻占b之后才可以攻占a;

Output

天策军最大能获得多少战斗收益。

Sample Input

5 5 
8 
-8 
-10 
12 
-10 
1 2 
2 5 
1 4 
3 4 
4 5

Sample Output

2

Source

2009 Multi-University Training Contest 16 – Host by NIT

没人告诉我这题多组数据啊qaq

题解:

要求找出权值最大的子图,满足子图中所有点的前驱都在同一子图内。

符合最大权闭合子图的定义,于是反向建边跑了一波……

样例没过。

噢给出的图是返向的,直接建边就好了。求出给定图的最大权闭合子图的权值,就是这题答案。

最大权闭合子图

子图

对于图\(G=(V,E)\),\(G_1=(V_1,E_1)\),有\(V_1\subseteq V\),\(E_1\subseteq E\),则称\(G_1\)是\(G\)的一个子图。

闭合图

对于图\(G=(V,E)\),\(G_1=(V_1,E_1)\subseteq G\),满足\(\forall \langle u,v\rangle\in E_1,u\in V_1\),有\(v\in V_1\)。也就是说,若图中每个点的后继(出边指向的点)都还应该在这个图中,则这个子图被称为闭合图。

构造方案

当然了,在最大权闭合子图的问题中,点权一定有正有负。不然选择所有点\(u\in V_1\)就可以满足最大了。

然后把正权点连向源点,负权点连向汇点(容量为点权的绝对值)。把图中原来存在的边还按原来的方向,容量置为\(\infty\)。此时跑出来最小割,与源点相连的点集以及仅在它们之间产生的边所构成的图就是最大权闭合子图了。

事实上,这个图的任意一个割把图分成\({s},{t}\)两部分时,\({s}-S\)(\(S\)是超级源点,\(-\)是减号)都满足是原图的一个闭合子图,这一证明可以在胡伯涛2007年的论文中找到。也可以和下面的描述一起理解这一过程。

理解

假设当前已经跑出来一个割,那么割边一定有一个顶点是源点或汇点。一定不是剩下的边,剩下的边容量都是\(\infty\),割掉它们肯定没有割掉其他边优。

当割掉源点有关的边时,代表了把这条边的另一个点分到\({t}\)去(否则也不会割它),也就是放弃了这个点的正值带来的收益。当割掉汇点有关的边时,代表了把这个点分到\({s}\),也就是承受了这个点的负值带来的影响。

此时源点那一部分点\({s}\)所构成的图就是一个闭合图。因为割边全部都与源点汇点有关,所以属于\({s}​\)集合的点的出边一定不属于割集。

当一个点为正值(与\(S\)有连边)且属于\({s}\)时,它一定可以朝着\(T\)方向扩展,它的出边没有被割掉,截流的位置一定在\({t}\)端。当一个点为负值(与\(T\)有连边)且属于\({s}\)时,它与\(T\)的连边一定被断掉了,它的后继也一定在图中。

割边表示付出的代价,和最小点权覆盖集一样。我们在最大权闭合子图中期望获得所有正值,但是由于需要最小割的代价,所以减去这个代价就是答案了。

Code:

#include<cstdio>
#include<cstring>
int Min(int x,int y)
{
    return x<y?x:y;
}
struct edge
{
    int n,nxt,v;
    edge(int n,int nxt,int v)
    {
        this->n=n;
        this->nxt=nxt;
        this->v=v;
    }
    edge(){}
}e[300000];
int head[505],ecnt=-1;
void add(int from,int to,int v)
{
    e[++ecnt]=edge(to,head[from],v);
    head[from]=ecnt;
    e[++ecnt]=edge(from,head[to],0);
    head[to]=ecnt;
}
int d[505],q[505];
bool bfs()
{
    int l=0,r=0;
    memset(d,0,sizeof(d));
    q[++r]=501;
    d[501]=1;
    while(l<r)
    {
        int x=q[++l];
        for(int i=head[x];~i;i=e[i].nxt)
            if(e[i^1].v&&!d[e[i].n])
            {
                d[e[i].n]=d[x]+1;
                q[++r]=e[i].n;
            }
    }
    return d[0]>0;
}
int dinic(int x,int in)
{
    if(x==501)
        return in;
    int flow=in;
    for(int i=head[x];flow&&~i;i=e[i].nxt)
        if(e[i].v&&d[e[i].n]==d[x]-1)
        {
            int tmp=dinic(e[i].n,Min(e[i].v,flow));
            if(!tmp)
                d[e[i].n]=0;
            e[i].v-=tmp;
            e[i^1].v+=tmp;
            flow-=tmp;
        }
    return in-flow;
}
int a[505],ans=0;
bool used[505];
int main()
{
    int n,m,u,v;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(head,-1,sizeof(head));
        memset(used,0,sizeof(used));
        ecnt=-1,ans=0;
        for(int i=1;i<=n;++i)
        {
            scanf("%d",&a[i]);
            if(a[i]<0)
                add(i,501,-a[i]);
            else
            {
                add(0,i,a[i]);
                ans+=a[i];
            }
        }
        for(int i=1;i<=m;++i)
        {
            scanf("%d%d",&u,&v);
            add(u,v,0x3fffffff);
        }
        while(bfs())
        {
            int tmp;
            do
            {
                tmp=dinic(0,0x3fffffff);
                ans-=tmp;
            }while(tmp);
        }
        printf("%d\n",ans);
    }
    return 0;
}

说点什么

avatar
  Subscribe  
提醒
/* */