现在的位置: 首页 > 综合 > 正文

上下界网络流

2013年10月11日 ⁄ 综合 ⁄ 共 3182字 ⁄ 字号 评论关闭

    · 博主智商拙计

    · 代码各种丑

    · 真· 原 (piao) 创 (qie)

    连考了二次上下界网络流……

    还好第一次考完后马上就恶补了> <~

    不过今天又有点晕 (kun) ,  于是写下来以防又悲剧......

    推荐:

        1、《一种简易的方法求解流量有上下界的网络中网络流问题》( 周源 )

        2、http://wenku.baidu.com/view/042856687e21af45b307a875.html


    建模什么的就不啰嗦了……反正要满足流量平衡.

    考虑没有下界的限制, 那么每条边的容量应当是 max(u, v) - min(u, v), 这些边构成图 G ' 

    然后我们设图 G ' 中的一条可行流 f, 其在边 e(u, v) 的流量为 g(u, v)

    显然如果想把这条可行流映射到原图中, 必然要满足以下的流量平衡条件 :

    ∑(min(v, i) + g(v, i)) = ∑ (min(i, u) + g(i, u))

    将上式变形, 可得到:

    ∑(min(v, i) - min(i, u)) = ∑ (g(i, u) - g(v, i))

    回到图 G ' , 假设图 G ‘ 的流没有满足流量平衡……也就是说∑ (g(i, u) - g(v, i)) ≠ 0,  那么很明显的是, 我们可以虚拟一个源或者汇, 流入其不足的流量, 或者流出其多余的流量.

    而流入的流量 / 流出的流量的大小正好为 ∑ (g(i, u) - g(v, i)) = ∑(min(v, i) - min(i, u))

    于是……新建一个源 _S, 一个汇 _T,  按照上述方法连边. 然后跑 _S -> _T 的最大流, 如果 _S 的所有出边以及 _T 的所有入边都满流, 那么表示流量平衡条件满足.

    具体的构造方法是 :

        1、先把原图变成无源无汇, 也就是T -> S 连一条容量为 ∞ 的边   ( 不然加入新源汇的话就有多个源、汇了……)

        2、建立边 e (u, v), 容量c (u, v) = max(u, v) - min(u, v)

        3、点 i 的 ∑(min(v, i) - min(i, u)) > 0 则连一条边e ( _S, i) , 容量 c ( _S, i) = ∑(min(v, i) - min(i, u))

              反之连一条边 e ( i, _T )

    然后跑 _S -> _T 最大流即可.  跑出来的最大流满足原图的流量平衡条件, 也就是说是原图的一条可行流.

    

    拓展:

    求原图最大流:跑完 _S -> _T 后, 再跑 S -> T 即可.  ( 很不理解为什么论文都说要二分……明明求出可行流了直接继续增广就可以了嘛 )

    最小费用最大流 :同上. 带费用而已.

    参考:CSDN Delayyy [例题] 最小矩阵差 - 有上下界的网络流问题

                http://blog.csdn.net/delayyy/article/details/8660158


    代码的话呢……

    那就放 Delayyy君的例题吧……

    写得好丑> <

    而且一直忘记加 e (T, S) 了……

    

#include <cstdio>
#include <cstdlib>
#include <algorithm>
const int INF = ~0U>>1;
using namespace std;

int a[210][210];
int c[100010], edv[100010], next[100010];
int g[1010], dima[1010], t[1010];
int tot[1010], p[210][210];
int n, m, min_p, max_p, min_r, S, T, sg, set, _S, _T;

void add(int x, int y, int t)
{
    c[++sg] = y, next[sg] = g[x], g[x] = sg, edv[sg] = t;
    c[++sg] = x, next[sg] = g[y], g[y] = sg, edv[sg] = 0;
}
bool BFS(int sta, int ent)
{
    int head = 1, tail = 1, p[1010];  p[1] = sta;  dima[sta] = 0;  t[sta] = ++set;
    for (; head <= tail; ++head)
      for (int x = g[p[head]]; x; x = next[x])
        if (t[c[x]] != set  &&  edv[x ^ 1])
          {
              p[++tail] = c[x];  dima[c[x]] = dima[p[head]] + 1;  t[c[x]] = set;
              if (c[x] == ent)  return true; 
          }    
    return false;
}
int DFS(int z, int push, int ent)
{
    if (z == ent  ||  !push)  return push;
    int f, flow = 0;
    for (int x = g[z]; x; x = next[x])
      if (edv[x]  &&  t[c[x]] == set)
        if (dima[z] == dima[c[x]] + 1  &&  (f = DFS(c[x], push > edv[x] ? edv[x] : push, ent)))
          {
              flow += f;  push -= f;
              edv[x] -= f;  edv[x ^ 1] += f;
              if (!push)  return flow;
          }    
    dima[z] = INF;
    return flow;
}
bool compare(int j)
{
    int tot_S = 0, tot_T = 0, ans = 0, x = 0, y = 0;
    sg = 1; 
    for (int i = 0; i <= n + m + 3; ++i)  g[i] = 0;
    for (int i = 1; i <= n; ++i)
      for (int j = 1; j <= m; ++j)
        add(i, j + n, max_p - min_p);
    for (int i = 1; i <= n; ++i)  
      {
          int sep = tot[i] > j ? tot[i] - j : 0;  x += sep;
          add(S, i, tot[i] + j - sep);
          if ((sep -= m * min_p) > 0)  
            add(_S, i, sep), tot_S += sep;
          else  if (sep < 0)
            add(i, _T, -sep), tot_T -= sep;
      }        
    for (int i = n + m; i > n; --i)
      {
          int sep = tot[i] > j ? tot[i] - j : 0;  y += sep;
          add(i, T, tot[i] + j - sep);
          if ((sep -= n * min_p) > 0)
            add(i, _T, sep), tot_T += sep;
          else  if (sep < 0)
            add(_S, i, -sep), tot_S -= sep;
      }     
    add(S, _T, x);  tot_T += x;  add(_S, T, y);  tot_S += y;  add(T, S, INF);
    while (BFS(_T, _S))  ans += DFS(_S, INF, _T);
    if (ans != tot_S)  return false;
    return true;
}
int main()
{
    freopen("mat.in", "r", stdin);
    freopen("mat.out", "w", stdout);
    scanf("%d %d", &n, &m);  _S = n + m + 2, _T = n + m + 3;  S = 0;  T = n + m + 1;
    for (int i = 1; i <= n; ++i)
      for (int j = 1; j <= m; ++j)
        scanf("%d", &min_p), min_r += min_p,
        tot[j + n] += min_p, tot[i] += min_p;
    scanf("%d %d", &min_p, &max_p);
    int l = 0, r = min_r, j, ans;
    for (; j = (l + r) >> 1, l <= r; )
      if (compare(j))  ans = j, r = j - 1;
      else  l = j + 1;
    compare(ans);  printf("%d", ans);
    for (int i = 1; i <= n; ++i)
      for (int x = g[i]; x; x = next[x])
        if (c[x] > n  &&  c[x] <= n + m)
          p[i][c[x] - n] = edv[x ^ 1];
    for (int i = 1; printf("\n"), i <= n; ++i)
      for (int j = 1; j <= m; ++j)
        printf("%d ", p[i][j] + min_p);
}



抱歉!评论已关闭.