HDU 3061 Battle(最小割 之 最大权闭包)经典

Battle

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 983    Accepted Submission(s): 477

Problem Description

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

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

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

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

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

Input

首先输入一个N 代表有N个城池(1<= n <= 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

解题:在一个图中,即有收益 又有花费,那么就可以用到最大权闭包,建图模型:收益的点与源点相连,边权为收益大小,花费的点与汇点相连,边权为花费大小(如果为负数则变为正数),其他原图的边的边权为INF。之后  ans = 总收益 - 最小割容量。

#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
#define captype int

const int MAXN = 100010;   //点的总数
const int MAXM = 400010;    //边的总数
const int INF = 1<<30;
struct EDG{
    int to,next;
    captype cap,flow;
} edg[MAXM];
int eid,head[MAXN];
int gap[MAXN];  //每种距离(或可认为是高度)点的个数
int dis[MAXN];  //每个点到终点eNode 的最短距离
int cur[MAXN];  //cur[u] 表示从u点出发可流经 cur[u] 号边
int pre[MAXN];

void init(){
    eid=0;
    memset(head,-1,sizeof(head));
}
//有向边 三个参数,无向边4个参数
void addEdg(int u,int v,captype c,captype rc=0){
    edg[eid].to=v; edg[eid].next=head[u];
    edg[eid].cap=c; edg[eid].flow=0; head[u]=eid++;

    edg[eid].to=u; edg[eid].next=head[v];
    edg[eid].cap=rc; edg[eid].flow=0; head[v]=eid++;
}
captype maxFlow_sap(int sNode,int eNode, int n){//n是包括源点和汇点的总点个数,这个一定要注意
    memset(gap,0,sizeof(gap));
    memset(dis,0,sizeof(dis));
    memcpy(cur,head,sizeof(head));
    pre[sNode] = -1;
    gap[0]=n;
    captype ans=0;  //最大流
    int u=sNode;
    while(dis[sNode]<n){   //判断从sNode点有没有流向下一个相邻的点
        if(u==eNode){   //找到一条可增流的路
            captype Min=INF ;
            int inser;
            for(int i=pre[u]; i!=-1; i=pre[edg[i^1].to])    //从这条可增流的路找到最多可增的流量Min
            if(Min>edg[i].cap-edg[i].flow){
                Min=edg[i].cap-edg[i].flow;
                inser=i;
            }
            for(int i=pre[u]; i!=-1; i=pre[edg[i^1].to]){
                edg[i].flow+=Min;
                edg[i^1].flow-=Min;  //可回流的边的流量
            }
            ans+=Min;
            u=edg[inser^1].to;
            continue;
        }
        bool flag = false;  //判断能否从u点出发可往相邻点流
        int v;
        for(int i=cur[u]; i!=-1; i=edg[i].next){
            v=edg[i].to;
            if(edg[i].cap-edg[i].flow>0 && dis[u]==dis[v]+1){
                flag=true;
                cur[u]=pre[v]=i;
                break;
            }
        }
        if(flag){
            u=v;
            continue;
        }
        //如果上面没有找到一个可流的相邻点,则改变出发点u的距离(也可认为是高度)为相邻可流点的最小距离+1
        int Mind= n;
        for(int i=head[u]; i!=-1; i=edg[i].next)
        if(edg[i].cap-edg[i].flow>0 && Mind>dis[edg[i].to]){
            Mind=dis[edg[i].to];
            cur[u]=i;
        }
        gap[dis[u]]--;
        if(gap[dis[u]]==0) return ans;  //当dis[u]这种距离的点没有了,也就不可能从源点出发找到一条增广流路径
                                        //因为汇点到当前点的距离只有一种,那么从源点到汇点必然经过当前点,然而当前点又没能找到可流向的点,那么必然断流
        dis[u]=Mind+1;//如果找到一个可流的相邻点,则距离为相邻点距离+1,如果找不到,则为n+1
        gap[dis[u]]++;
        if(u!=sNode) u=edg[pre[u]^1].to;  //退一条边
    }
    return ans;
}
int main()
{
    int n,m,u,v,c;
    while(scanf("%d%d",&n,&m)>0)
    {
        init();
        int vs=0,vt=n+1 , ans=0;
        for(int i=1; i<=n; i++){
            scanf("%d",&c);
            if(c>=0)
                ans+=c,addEdg(vs , i , c);
            else
                addEdg(i , vt , -c);
        }
        while(m--){
            scanf("%d%d",&u,&v);
            addEdg(u , v , INF);
        }
        ans-=maxFlow_sap(vs , vt , vt+1);
        printf("%d\n",ans);
    }
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-02 05:54:54

HDU 3061 Battle(最小割 之 最大权闭包)经典的相关文章

HDU 3061 Battle(最小割----最大权闭合图)

题目地址:HDU 3061 多校中遇到的最小割的最大权闭合模型花了一上午时间终于看懂啦. 最大权闭合图就是将一些互相有依赖关系的点转换成图,闭合图指的是在图中的每一个点的后继点都是在图内的. 还要明白简单割的概念,就是指所有的割边都与源点或汇点相连.然后让源点与正权点相连,汇点与负权点相连,权值均为其绝对值,有依赖关系的点连一条有向边,如果a必须在b的基础上,那么就连一条a->b的有向边,权值为INF.最后用所有正权值得和减去最小割的值就是答案. 具体证明可看胡伯涛大牛的国家队集训论文<最小割

HDU 4971 A simple brute force problem.(最小割,最大权闭合图)

http://acm.hdu.edu.cn/showproblem.php?pid=4971 A simple brute force problem. Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 182    Accepted Submission(s): 115 Problem Description There's a com

【HDU 5855】Less Time, More profit(网络流、最小割、最大权闭合子图)

Less Time, More profit Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Problem Description The city planners plan to build N plants in the city which has M shops. Each shop needs products from some plants to make p

HDU 4859 海岸线 最小割

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4859 题解: 这题考察的是最小割. 我们可以这样想:海岸线的长短变化都是E引起的,我们通过把’E'变成'.'或'D'来使海岸线最大化. 我们要算海岸线就是算格子‘.'和格子'D'(在原有地图周围四面都要加’D‘)相邻的面数,要使它最大,就是要使'.'与’.':'D'与'D'相邻的面数最小,而面数最小可以用最小割来做. 现在我们先把格子上的点黑白染色,(i+j)%2==1的为A类,为0的为B类, 在

BZOJ1565 NOI 2009 植物大战僵尸 topo+最小割(最大权闭合子图)

题目链接:https://www.luogu.org/problemnew/show/P2805(bzoj那个实在是有点小小的辣眼睛...我就把洛谷的丢出来吧...) 题意概述:给出一张有向图,这张有向图上的每个点都有一个点权,想要访问某个点必须要先访问这个点所能够访问(遍历)到的所有点,在访问到一个点之后将会得到这个点的权值(可正可负).问访问这张图可以得到的最大点权和. 原题说过来说过去实际上是描述了一个植物之间的保护关系,也就是说明了植物之间的先后访问顺序之间的关系.可以描述为要"要访问点

网络流——最小割求最大权闭合子图

定义 有一个有向图,每一个点都有一个权值(可以为正或负或0),选择一个权值和最大的子图,使得每个点的后继都在子图里面,这个子图就叫最大权闭合子图. 如下图:  能选的子图有?,{4},{3,4},{2,4},{1,2,3,4},它们的权值分别为0,-1,5,-6,4. 所以最大权闭合子图为{3,4},权值为5. 解法 这个问题可以转化为最小割问题,用网络流解决. 从源点s向每个正权点连一条容量为权值的边,每个负权点向汇点t连一条容量为权值的绝对值的边,有向图原来的边容量全部为无限大.  求它的最

hdu 3987 求最小割条数最小

题意:    一个人要从起点  0  到达 n-1   n个点  m条路  ,我们求最少破坏路的条数使无法 从起点到达终点.题意很明显  ,求最小割条数最少,由于最小割流量虽然固定,但是其条数却不固定,可以破坏3条路,也可以破坏4条路,他们总流量相同才会出现这种情况. 题解:由于上述的情况,他们总流量相同但是条数不同,现在我们需要改变边的容量使得条数少边才是最小割,条数多的将不会是最小割. 官方题解有两种 ,我选择的是在残余网络中进行扩充流的操作,使的两个最小割不同,残余网络中,我进行所有边流量

hdu 3657 Game 最小割

首先经典的奇偶建立二分图(X,Y),对于相邻两点连边2*(X&Y),源->X连边,Y->汇连边,权值w为点权,求最小割. 考虑一条路径 源->X->Y->汇 若割边选取的是源->X,则表示选Y点不选X点, 答案为w(X+Y)-w(X) 若割边选取的是Y->,则表示选X点不选Y点, 答案为w(X+Y)-w(Y) 若割边选取的是X->Y,则表示选Y点且选X点, 答案为w(X+Y)-w( 2*(X&Y) ) 即总点权-最小割 #include<

HDU - 4289 Control (最小割 MCMF)

题目大意:有一个间谍要将一些机密文件送到目的地 现在给出间谍的初始位置和要去的目的地,要求你在间谍的必经路上将其拦获且费用最小 解题思路:最小割最大流的应用,具体可以看网络流–最小割最大流 建图的话 超级源点–起始城市,容量为INF 城市拆成两点(u, v),容量为监视该城市的代价 能连通的城市连接,容量为INF 目的地和超级汇点相连,容量为INF #include <cstdio> #include <cstring> #include <algorithm> #in