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

题目链接

题意:

由于小白同学近期习武十分刻苦,很快被晋升为天策军的统帅。而他上任的第一天,就面对了一场极其困难的战斗: 
据侦查兵回报,前方共有N座城池,考虑到地势原因,最终得到一个结论:攻占某些城池之前必须攻占另外一些城池。 
事实上,可以把地图看做是一张拓扑图,而攻占某个城池,就意味着必须先攻占它的所有前驱结点。 
小白还做了一份调查,得到了攻占每个城池会对他的兵力产生多少消耗(当然也可能会得到增长,因为每攻占一个城池,便可以整顿军队,扩充兵力,天策军的兵力十分庞大,如果不考虑收益,他们可以攻取所有的城池)。 
现在请你帮小白统帅做一份战斗计划,挑选攻打哪些城市,使得天策军在战斗过后军容最为壮大。

思路:

一开始一直没有想出来,后来看了蒟巨的网络流建图博客才发现,这就是一个裸的最大权闭合子图问题(还是太菜了呀-very vegetable);

顺便再补充一下最大权闭合子图的概念:

参考:胡伯涛 《最小割模型在信息学竞赛中的应用》

定义:

有向图的闭合图:闭合图内任一点的任意后继结点也一定还在闭合图中。

物理意义:事物间的依赖关系,一件事情要发生,它需要的所有前提条件也都一定要发生。

最大权闭合图:点权之和最大的闭合图。

建图:

设置超级源汇S,T。

然后使$S$和所有的正权的点连接权值为点权的边,所有点权为负的点和$T$连接权值为点权绝对值的边。然后如果选择了某个$v$点才可以选$u$点,那么把u向v连接一条权值为$\infty$的边。

最大点权和=正点权和-最小割。证明略

  1 /*
  2 *  Author: windystreet
  3 *  Date  : 2018-08-20 09:25:36
  4 *  Motto : Think twice, code once.
  5 */
  6 #include<bits/stdc++.h>
  7 
  8 using namespace std;
  9 
 10 #define X first
 11 #define Y second
 12 #define eps  1e-5
 13 #define gcd __gcd
 14 #define pb push_back
 15 #define PI acos(-1.0)
 16 #define lowbit(x) (x)&(-x)
 17 #define bug printf("!!!!!\n");
 18 #define mem(x,y) memset(x,y,sizeof(x))
 19 
 20 typedef long long LL;
 21 typedef long double LD;
 22 typedef pair<int,int> pii;
 23 typedef unsigned long long uLL;
 24 
 25 const int maxn = 505;
 26 const int INF  = 1<<30;
 27 const int mod  = 1e9+7;
 28 
 29 struct Edge{
 30      int from,to,cap,flow;
 31 };
 32 
 33 struct Dinic
 34 {
 35     int n,m,s,t;
 36     vector<Edge>edge;
 37     vector<int>G[maxn];
 38     bool vis[maxn];
 39     int d[maxn];
 40     int cur[maxn];
 41     void init(int n){
 42         this->n = n;
 43         for(int i=0;i<=n;i++)G[i].clear(),edge.clear();
 44     }
 45     inline void addedge(int from,int to,int cap){
 46         edge.pb((Edge){from,to,cap,0});
 47         edge.pb((Edge){to,from,0,0});
 48         m = edge.size();
 49         G[from].pb(m-2);
 50         G[to].pb(m-1);
 51     }
 52     inline bool bfs(){
 53         mem(vis,0);
 54         queue<int>Q;
 55         Q.push(s);
 56         d[s] = 0;
 57         vis[s] = 1;
 58         while(!Q.empty()){
 59             int x = Q.front(); Q.pop();
 60             int sz = G[x].size();
 61             for(int i=0;i<sz;++i){
 62                 Edge &e = edge[G[x][i]];
 63                 if(!vis[e.to] && e.cap>e.flow){
 64                     vis[e.to] = 1 ;
 65                     d[e.to] = d[x] + 1;
 66                     Q.push(e.to); 
 67                 }
 68             }
 69         }
 70         return vis[t];
 71     }
 72     inline int dfs(int x,int a){
 73         if(x == t || a == 0)return a;
 74         int flow = 0,f;
 75         int sz = G[x].size();
 76         for(int &i = cur[x];i<sz;i++){
 77             Edge &e = edge[G[x][i]];
 78             if(d[x] + 1 == d[e.to] && (f = dfs(e.to,min(a,e.cap - e.flow)))>0){
 79                 e.flow += f;
 80                 edge[G[x][i]^1].flow -=f;
 81                 flow += f;
 82                 a -= f;
 83                 if(a==0)break;
 84             }
 85         }
 86         if(!flow) d[x] = -2;  //炸点优化
 87         return flow;
 88     }
 89     inline int maxflow(int s,int t){
 90         this->s = s; this -> t = t;
 91         int flow = 0;
 92         while(bfs()){
 93             mem(cur,0);
 94             flow += dfs(s,INF);
 95         }
 96         return flow;
 97     }
 98 };
 99 
100 int s[maxn];
101 void solve(){
102     int n,m,sum,x,y;
103     while(scanf("%d%d",&n,&m)!=EOF){
104         Dinic dinic;
105         sum = 0;
106         int S = 0,T = n+2;
107         for(int i=1;i<=n;i++){
108             scanf("%d",s+i);
109             if(s[i]>0){
110                 sum+=s[i];
111                 dinic.addedge(S,i,s[i]);        // 权值为正的点与源点S相连
112             }else{
113                 dinic.addedge(i,T,-s[i]);       // 否则与汇点T相连
114             }
115         }
116         for(int i=1;i<=m;i++){
117             scanf("%d%d",&x,&y);
118             dinic.addedge(x,y,INF);             // 在y的前提下发生x,则加入边x->y,权值为inf
119         }
120         printf("%d\n",sum-dinic.maxflow(S,T));
121     }
122 
123     return;
124 }
125 
126 int main()
127 {
128 //    freopen("F:\\in.txt","r",stdin);
129 //    freopen("out.txt","w",stdout);
130 //    ios::sync_with_stdio(false);
131     int t = 1;
132     //scanf("%d",&t);
133     while(t--){
134     //    printf("Case %d: ",cas++);
135         solve();
136     }
137     return 0;
138 }

 

posted @ 2018-08-20 11:34  windystreet  阅读(186)  评论(0编辑  收藏  举报