zoukankan      html  css  js  c++  java
  • [HIHO119]网络流五·最大权闭合子图(最大流)

    题目链接:http://hihocoder.com/contest/hiho119/problem/1

    题意:中文题意。

    由于1≤N≤200,1≤M≤200。最极端情况就是中间所有边都是满的,一共有N*M条边,则最多有40000条边。

    对于这样的问题有统一的建图策略,提取出问题的最终形态即可按照如下方式建图:

    首先建立源点s和汇点t,将源点s与所有权值为正的点相连,容量为权值;将所有权值为负的点与汇点t相连,容量为权值的绝对值;权值为0的点不做处理;同时将原来的边容量设置为无穷大。

    利用结论:最大权闭合子图的权值等于所有正权点之和减去最小割。

    证明:

    1. 最小割一定是简单割

    简单割指得是:割(S,T)中每一条割边都与s或者t关联,这样的割叫做简单割。

    因为在图中将所有与s相连的点放入割集就可以得到一个割,且这个割不为正无穷。而最小割一定小于等于这个割,所以最小割一定不包含无穷大的边。因此最小割一定一个简单割。

    2. 简单割一定和一个闭合子图对应

    闭合子图V和源点s构成S集,其余点和汇点t构成T集。

    首先证明闭合子图是简单割:若闭合子图对应的割(S,T)不是简单割,则存在一条边(u,v),u∈S,v∈T,且c(u,v)=∞。说明u的后续节点v不在S中,产生矛盾。

    接着证明简单割是闭合子图:对于V中任意一个点u,u∈S。u的任意一条出边c(u,v)=∞,不会在简单割的割边集中,因此v不属于T,v∈S。所以V的所有点均在S中,因此S-s是闭合子图。

    由上面两个引理可以知道,最小割也对应了一个闭合子图,接下来证明最小割就是最大权的闭合子图。

    首先有割的容量C(S,T)=T中所有正权点的权值之和+S中所有负权点的权值绝对值之和。

    闭合子图的权值W=S中所有正权点的权值之和-S中所有负权点的权值绝对值之和。

    则有C(S,T)+W=T中所有正权点的权值之和+S中所有正权点的权值之和=所有正权点的权值之和。

    所以W=所有正权点的权值之和-C(S,T)

    由于所有正权点的权值之和是一个定值,那么割的容量越小,W也就越大。因此当C(S,T)取最小割时,W也就达到了最大权。

      1 #include <bits/stdc++.h>
      2 using namespace std;
      3 
      4 typedef struct Edge {
      5     int u, v, w, next;
      6 }Edge;
      7 
      8 const int inf = 0x7f7f7f7f;
      9 const int maxn = 500;
     10 
     11 int cnt, dhead[maxn];
     12 int cur[maxn], dd[maxn];
     13 Edge dedge[maxn*maxn];
     14 int S, T, N;
     15 int n, m;
     16 
     17 
     18 void init() {
     19     memset(dhead, -1, sizeof(dhead));
     20     for(int i = 0; i < maxn; i++) dedge[i].next = -1;
     21     cnt = 0;
     22 }
     23 
     24 void adde(int u, int v, int w, int c1) {
     25     dedge[cnt].u = u; dedge[cnt].v = v; dedge[cnt].w = w; 
     26     dedge[cnt].next = dhead[u]; dhead[u] = cnt++;
     27     dedge[cnt].u = v; dedge[cnt].v = u; dedge[cnt].w = c1; 
     28     dedge[cnt].next = dhead[v]; dhead[v] = cnt++;
     29 }
     30 
     31 bool bfs(int s, int t, int n) {
     32     queue<int> q;
     33     for(int i = 0; i < n; i++) dd[i] = inf;
     34     dd[s] = 0;
     35     q.push(s);
     36     while(!q.empty()) {
     37         int u = q.front(); q.pop();
     38         for(int i = dhead[u]; ~i; i = dedge[i].next) {
     39             if(dd[dedge[i].v] > dd[u] + 1 && dedge[i].w > 0) {
     40                 dd[dedge[i].v] = dd[u] + 1;
     41                 if(dedge[i].v == t) return 1;
     42                 q.push(dedge[i].v);
     43             }
     44         }
     45     }
     46     return 0;
     47 }
     48 
     49 int dinic(int s, int t, int n) {
     50     int st[maxn], top;
     51     int u;
     52     int flow = 0;
     53     while(bfs(s, t, n)) {
     54         for(int i = 0; i < n; i++) cur[i] = dhead[i];
     55         u = s; top = 0;
     56         while(cur[s] != -1) {
     57             if(u == t) {
     58                 int tp = inf;
     59                 for(int i = top - 1; i >= 0; i--) {
     60                     tp = min(tp, dedge[st[i]].w);
     61                 }
     62                 flow += tp;
     63                 for(int i = top - 1; i >= 0; i--) {
     64                     dedge[st[i]].w -= tp;
     65                     dedge[st[i] ^ 1].w += tp;
     66                     if(dedge[st[i]].w == 0) top = i;
     67                 }
     68                 u = dedge[st[top]].u;
     69             }
     70             else if(cur[u] != -1 && dedge[cur[u]].w > 0 && dd[u] + 1 == dd[dedge[cur[u]].v]) {
     71                 st[top++] = cur[u];
     72                 u = dedge[cur[u]].v;
     73             }
     74             else {
     75                 while(u != s && cur[u] == -1) {
     76                     u = dedge[st[--top]].u;
     77                 }
     78                 cur[u] = dedge[cur[u]].next;
     79             }
     80         }
     81     }
     82     return flow;
     83 }
     84 
     85 int main() {
     86     // freopen("in", "r", stdin);
     87     int k, u, v, w;
     88     while(~scanf("%d %d", &n, &m)) {
     89         init(); S = 0; T = n + m + 1; N = T + 1;
     90         int pos = 0;
     91         for(int i = 1; i <= m; i++) {
     92             scanf("%d", &w);
     93             if(w != 0) adde(n+i, T, w, 0);
     94             else adde(n+i, T, inf, 0);
     95         }
     96         for(int i = 1; i <= n; i++) {
     97             scanf("%d %d", &w, &k);
     98             pos += w;
     99             if(w != 0) adde(S, i, w, 0);
    100             else adde(S, i, inf, 0);
    101             for(int j = 0; j < k; j++) {
    102                 scanf("%d", &v);
    103                 adde(i, n+v, inf, 0);
    104             }
    105         }
    106         printf("%d
    ", pos - dinic(S,T,N));
    107     }
    108 }
  • 相关阅读:
    软件实施工程师是一个什么样的工作?他的具体工作内容是什么?发展前景怎样?
    做金融(基金、证券)方面的软件实施工程师有没有发展前途?职业发展空间如何。
    做软件实施工程师的一点建议
    系统实施工程师主要工作职则
    软件实施工程师
    UE编辑器编译和运行java设置
    猜数字
    猜数字
    Problem G
    Problem G
  • 原文地址:https://www.cnblogs.com/kirai/p/5952667.html
Copyright © 2011-2022 走看看