zoukankan      html  css  js  c++  java
  • [ZJOI2011]最小割 & [CQOI2016]不同的最小割 分治求最小割

    题面:

    [ZJOI2011]最小割

    [CQOI2016]不同的最小割

    题解:

    其实这两道是同一道题。。。。

    最小割是用的dinic,不同的最小割是用的isap

    其实都是分治求最小割

    简单讲讲思路吧

    就是首先全部的点都在一个集合里,然后随意定两个点为s和t,这里默认是第一个和最后一个。

    然后找到最小割,最小割将整张图分为了s集和t集,于是我们再用这个最小割更新跨集合点对之间的最小割。

     这个很好理解,因为当前找到的最小割将s集和t集分开了,显然对于任意一组跨集合的点对而言,当前最小割都是一个可能的最小割。

    然后我们再递归处理s集和t集(重复以上步骤)。

    每次找到最小割后就更新跨集合点对。

    本质上是分治吧。

    之前看有些地方提到了最小割树,这里放个链接(这是我找到的写的最全的一篇了)

    Gomory-Hu tree 最小割树

    下面放代码吧,个人觉得看代码会更好理解,尤其是对分治不熟悉的人(比如我)

    不同的最小割(isap):

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define R register int
      4 #define AC 900
      5 #define ac 20000
      6 #define inf 2139062143
      7 #define D printf("line in %d
    ", __LINE__);
      8 char READ[7000100], *o = READ;
      9 int n, m, s, addflow, t, answer, tt;
     10 int last[AC], c[AC], have[AC], a[AC], ans[AC][AC], good[AC];
     11 int Head[AC], date[ac], Next[ac], haveflow[ac], tot = 1;
     12 int q[AC], head, tail;
     13 int ss[400000], cnt;
     14 bool z[AC];
     15 
     16 inline int read()
     17 {
     18     int x = 0; char c = getchar();
     19     while(c > '9' || c < '0') c = getchar();
     20     while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
     21     return x;
     22 }
     23 
     24 inline void add(int f, int w, int S)
     25 {
     26     date[++tot] = w, Next[tot] = Head[f], Head[f] = tot, haveflow[tot] = S;
     27     date[++tot] = f, Next[tot] = Head[w], Head[w] = tot, haveflow[tot] = S;
     28     //printf("%d ---> %d %d
    ", f, w, S);
     29 }
     30 
     31 inline void upmin(int &a, int b)
     32 {
     33     if(b < a) a = b;
     34 }
     35 
     36 void pre()
     37 {
     38     int u, v, e;
     39     n = read(), m = read();
     40     for(R i = 1; i <= n; i++) a[i] = i;
     41     for(R i = 1; i <= m; i++)
     42     {
     43         u = read(), v = read(), e = read();
     44         add(u, v, e);    
     45     }
     46     memset(ans, 127, sizeof(ans));
     47 }
     48 
     49 bool bfs()
     50 {
     51     int x, now;
     52     memset(have, 0, sizeof(have));
     53     memset(c, 0, sizeof(c));
     54     have[1] = 1, c[t] = 1, x = t;
     55     head = tail = 0;
     56     q[++tail] = t;
     57     while(head < tail)
     58     {
     59         x = q[++head]; 
     60         for(R i = Head[x]; i ; i = Next[i])
     61         {
     62             now = date[i];
     63             if(haveflow[i] && !c[now])
     64             {
     65                 c[now] = c[x] + 1;
     66                 ++have[c[now]];//error...忘记统计了
     67                 q[++tail] = now;
     68             }
     69         }
     70     }
     71     memcpy(good, Head, sizeof(Head));
     72     return c[s];
     73 }
     74 
     75 inline void aru()
     76 {
     77     int x = t;
     78     while(x != s)
     79     {
     80         haveflow[last[x]] -= addflow;
     81         haveflow[last[x] ^ 1] += addflow;
     82         x = date[last[x] ^ 1];
     83     }
     84     tt += addflow;
     85 }
     86 
     87 void isap()
     88 {
     89     int x = s, now; bool done;
     90     tt = 0, addflow = inf; 
     91     while(c[s] != 875)
     92     {
     93         if(x == t) aru(), addflow = inf, x = s;//忘记设置全局了,,,那在这里手动改一下吧
     94         done = false;
     95         for(R i = good[x]; i ; i = Next[i])
     96         {
     97             now = date[i];
     98             if(haveflow[i] && c[now] == c[x] - 1)
     99             {
    100                 upmin(addflow, haveflow[i]);
    101                 done = true;
    102                 last[now] = i;
    103                 good[x] = i;
    104                 x = now;
    105                 break;
    106             }
    107         }
    108         if(!done)
    109         {
    110             int go = 874;
    111             for(R i=Head[x]; i ; i = Next[i])
    112             {
    113                 now = date[i];
    114                 if(haveflow[i] && c[now]) upmin(go, c[now]);
    115             }
    116             good[x] = Head[x];
    117             if(!(--have[c[x]])) break;
    118             ++have[c[x] = go + 1];
    119             if(x != s) x = date[last[x] ^ 1];
    120         }
    121     }
    122 }
    123 
    124 void restore()//还原
    125 {
    126     for(R i = 2; i <= tot; i += 2)//对于无向图而言,这还是非常妙的
    127         haveflow[i] = haveflow[i ^ 1] = (haveflow[i] + haveflow[i ^ 1]) / 2;
    128 }
    129 
    130 void dfs(int x)
    131 {
    132     int now;
    133     z[x] = true;//....
    134     for(R i = Head[x]; i ; i = Next[i])
    135     {
    136         now = date[i];
    137         if(haveflow[i] && !z[now]) dfs(now);
    138     }
    139 }
    140 
    141 void solve(int l, int r)
    142 {
    143     if(l == r) return ;
    144     s = a[l], t = a[r];
    145     restore();
    146     //printf("%d %d
    ", l, r);
    147     bfs();//重新定层次
    148     isap();
    149     memset(z, 0, sizeof(z));
    150     dfs(s);
    151     for(R i=1;i<=n;i++)//更新最小割
    152         if(z[i])
    153             for(R j=1;j<=n;j++)
    154                 if(!z[j])
    155                     ans[i][j] = ans[j][i] = min(ans[i][j], tt);
    156     int ll = l - 1, rr = r + 1;
    157     for(R i = l; i <= r; i++)
    158         if(z[a[i]]) q[++ll] = a[i];
    159         else q[--rr] = a[i];//不知道取什么名字了,先借这个用一下吧
    160     for(R i = l; i <= r; i++) a[i] = q[i];
    161     solve(l, ll), solve(rr, r);
    162 }
    163 
    164 void work()
    165 {
    166     for(R i=1;i<=n;i++)
    167         for(R j=1;j<i;j++)
    168             ss[++cnt] = ans[i][j];
    169     sort(ss + 1, ss + cnt + 1);
    170     for(R i=1;i<=cnt;i++)
    171         if(ss[i] != ss[i + 1]) ++answer;
    172     printf("%d
    ", answer); 
    173 }
    174 
    175 int main()
    176 {
    177 //    freopen("in.in","r",stdin);
    178     pre();
    179     solve(1, n);
    180     work();
    181 //    fclose(stdin);
    182     return 0;
    183 }
    View Code

    最小割(dinic):

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define R register int
      4 #define LL long long
      5 #define inf 2139062143
      6 #define getchar() *o++
      7 #define AC 155
      8 #define ac 101000
      9 #define D printf("line in %d
    ", __LINE__);
     10 
     11 char READ[7001000], *o = READ;
     12 int s, t, T, Q, n, m;
     13 int last[AC], ans[AC][AC], a[AC], tmp[AC], c[AC];
     14 int date[ac], Next[ac], haveflow[ac], Head[AC], tot;
     15 int q[AC], head, tail;
     16 bool z[AC];
     17 
     18 inline int read()
     19 {
     20     int x = 0;char c = getchar();bool z = false;
     21     while(c > '9' || c < '0') c = getchar();
     22     while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
     23     if(!z) return x;
     24     else return -x;
     25 }
     26 
     27 inline void add(int f, int w, int S)
     28 {//因为是双向边,所以反向边就可以省掉了
     29     date[++tot] = w, Next[tot] = Head[f], Head[f] = tot, haveflow[tot] = S;
     30     date[++tot] = f, Next[tot] = Head[w], Head[w] = tot, haveflow[tot] = S;
     31 //    printf("%d ---> %d %d
    ", f, w, S);
     32 }
     33 
     34 bool bfs()
     35 {
     36     int now, x;
     37     head  = tail = 0;
     38     memset(c, 0, sizeof(c));
     39     c[s] = 1, q[++tail] = s;
     40     while(head < tail)
     41     {
     42         x = q[++head];
     43         for(R i = Head[x]; i ; i = Next[i])
     44         {
     45             now = date[i];
     46             if(haveflow[i] && !c[now])
     47             {
     48                 c[now] = c[x] + 1;
     49                 q[++tail] = now;
     50             }
     51         }
     52     }
     53     return c[t];
     54 }
     55 
     56 int dfs(int x, int flow)
     57 {
     58     if(x == t) return flow;
     59     int addflow, used = 0, now;
     60     for(R i=Head[x]; i ; i = Next[i])
     61     {
     62         now = date[i];
     63         if(c[now] == c[x] + 1)
     64         {//流到下一个点去的流量在haveflow和剩余流量中取最小值
     65             addflow = dfs(now, min(haveflow[i], flow - used));
     66             haveflow[i] -= addflow;
     67             haveflow[i^1] += addflow;
     68             used += addflow;
     69             if(used == flow) return flow;
     70         }
     71     }
     72     if(!used) c[x] = 0;
     73     return used;
     74 }
     75 
     76 int dinic()
     77 {
     78     int rnt = 0;
     79     while(bfs()) rnt += dfs(s, inf);
     80     return rnt;
     81 }
     82 
     83 void DFS(int x)//标记能到的点,便于区分
     84 {
     85     int now;
     86     z[x]= 1;
     87     for(R i = Head[x]; i ; i = Next[i])
     88     {
     89         now = date[i];
     90         if(haveflow[i] && !z[now]) DFS(now); 
     91     }
     92 }
     93 
     94 void restore()//挺妙的还原流量的方法,因为这两条边即是双向边,又可以当反向边
     95 {//因此总流量肯定是不会变的,所以加起来取个平均值就相当于还原了
     96     for(R i = 2; i <= tot; i += 2)
     97         haveflow[i] = haveflow[i+1] = (haveflow[i] + haveflow[i+1]) / 2;
     98 }
     99 
    100 void solve(int l, int r)
    101 {
    102     if(l == r) return ;
    103     restore();
    104     s = a[l], t = a[r];//随便选两个作为s和t
    105     int tt = dinic();//跑最小割
    106     memset(z, 0, sizeof(z));
    107     DFS(s);//查看哪些点是能到的(即没有被割断)
    108     for(R i=1;i<=n;i++)//这里是在每次找到一个最小割的时候,就对跨集合点对的割进行更新,
    109         if(z[i])//因此每次是需要枚举所有点对的。不然的话因为i是枚举的s集里面的东西,可能会导致某些点对没有被统计到
    110             for(R j=1;j<=n;j++)//获取跨集合点对的割的大小
    111                 if(!z[j]) ans[i][j] = ans[j][i] = min(ans[i][j], tt);
    112     int ll = l - 1, rr = r + 1;
    113     for(R i = l; i <= r; i++)
    114         if(z[a[i]])//如果这个点可以属于s集
    115             tmp[++ll] = a[i];//加入左边(从左边开始塞)
    116         else tmp[--rr] = a[i];//不然就放在右边
    117     for(R i=l;i<=r;i++) a[i] = tmp[i];//类似于归并排序的,,,放回数组内
    118     solve(l, ll), solve(rr, r);
    119 }
    120 
    121 void pre()
    122 {
    123     int u, v, k;
    124     tot  = 1;
    125     memset(ans, 127, sizeof(ans));
    126     memset(Head, 0, sizeof(Head));
    127     n = read(), m = read();
    128     for(R i=1;i<=n;i++) a[i] = i;//先把点按顺序放进来
    129     for(R i=1;i<=m;i++)
    130     {
    131         u = read(), v = read(), k = read();
    132         add(u, v, k);
    133     }
    134     solve(1, n);//找出所有最小割
    135 }
    136 
    137 void work()
    138 {
    139     int k, cnt;
    140     Q = read();
    141     while(Q--)
    142     {
    143         k = read(), cnt = 0;
    144         for(R i=1;i<=n;i++)
    145             for(R j=1;j<i;j++)//不能一个点对重复统计了
    146                 if(ans[i][j] <= k) ++cnt;
    147         printf("%d
    ", cnt);
    148     }
    149     printf("
    ");
    150 }
    151 
    152 int main()
    153 {
    154 //    freopen("in.in","r",stdin);
    155     fread(READ, 1, 7000000, stdin);
    156     T = read();
    157     while(T--)
    158     {
    159         pre();
    160         work();
    161     }
    162 //    fclose(stdin);
    163     return 0;
    164 }
    View Code
  • 相关阅读:
    注意力机制的命名实体识别
    tfidf_CountVectorizer 与 TfidfTransformer 保存和测试
    转载:UML学习(三)-----序列图(silent)
    转载:UML学习(二)-----类图(silent)
    转载:UML学习(一)-----用例图 (silent)
    转载:Practical UML™: A Hands-On Introduction for Developers
    转载:gc的概念,如果A和B对象循环引用,是否可以被GC?
    转载:Java高并发,如何解决,什么方式解决
    转载:消息队列应用场景
    转载:详解Java 自动装箱与拆箱的实现原理
  • 原文地址:https://www.cnblogs.com/ww3113306/p/9236049.html
Copyright © 2011-2022 走看看