网络流题目泛做(费用流的不写在这里面)

题目1 ZJOI 最小割

题目大意:

求一个无向带权图两点间的最小割,询问小于等于c的点对有多少。

算法讨论: 最小割 分治

代码:

#include <cstdlib>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>

using namespace std;
const int N = 150 + 5;
const int M = 3000 + 5;
const int oo = 0x3f3f3f3f;
#define inf oo

int n, m;
bool mark[N];
int a[N], tmp[N], ans[N][N];

struct Edge {
  int from, to, cap, flow;
  Edge(int u = 0, int v = 0, int c = 0, int f = 0) :
    from(u), to(v), cap(c), flow(f) {}
};

struct Dinic {
  int n, mm, s, t;
  int dis[N], cur[N], que[N * 10];
  bool vis[N];
  vector <Edge> edges;
  vector <int> G[N];

  void clear() {
    for(int i = 0; i <= n; ++ i) G[i].clear();
    edges.clear();
  }

  void add(int from, int to, int cap) {
    edges.push_back(Edge(from, to, cap, 0));
    edges.push_back(Edge(to, from, cap, 0));
    mm = edges.size();
    G[from].push_back(mm - 2);
    G[to].push_back(mm - 1);
  }

  bool bfs() {
    int head = 1, tail = 1;
    memset(vis, false, (n + 1) * sizeof (bool));
    dis[s] = 0; vis[s] = true; que[head] = s;
    while(head <= tail) {
      int x = que[head];
      for(int i = 0; i < (signed) G[x].size(); ++ i) {
        Edge &e = edges[G[x][i]];
        if(!vis[e.to] && e.cap > e.flow) {
          vis[e.to] = true;
          dis[e.to] = dis[x] + 1;
          que[++ tail] = e.to;
        }
      }
      ++ head;
    }
    return vis[t];
  }

  int dfs(int x, int a) {
    if(x == t || a == 0) return a;
    int flw = 0, f;
    for(int &i = cur[x]; i < (signed) G[x].size(); ++ i) {
      Edge &e = edges[G[x][i]];
      if(dis[e.to] == dis[x] + 1 && (f = dfs(e.to, min(a, e.cap - e.flow))) > 0) {
        e.flow += f; edges[G[x][i] ^ 1].flow -= f; flw += f; a -= f;
        if(a == 0) break;
      }
    }
    return flw;
  }

  int maxflow(int s, int t) {
    this->s = s; this->t = t;
    int flw = 0;
    while(bfs()) {
      memset(cur, 0, sizeof cur);
      flw += dfs(s, oo);
    }
    return flw;
  }

  void rebuild() {
    for(int i = 0; i < (signed) edges.size(); ++ i)
      edges[i].flow = 0;
  }

  void dfs(int u) {
    mark[u] = true;
    for(int i = 0; i < (signed) G[u].size(); ++ i) {
      Edge e = edges[G[u][i]];
      if(!mark[e.to] && e.cap > e.flow) {
        dfs(e.to);
      }
    }
  }
}net;

void Divide(int l, int r) {
  if(l >= r) return;
  net.rebuild();
  int nowflow = net.maxflow(a[l], a[r]);
  memset(mark, false, (n + 1) * sizeof (bool));
  net.dfs(a[l]);
  for(int i = 1; i <= n; ++ i)
    if(mark[i])
      for(int j = 1; j <= n; ++ j)
        if(!mark[j])
          ans[i][j] = min(ans[i][j], nowflow), ans[j][i] = ans[i][j];
  int L = l, R = r;
  for(int i = l; i <= r; ++ i)
    if(mark[a[i]]) tmp[L ++] = a[i];
    else tmp[R --] = a[i];
  for(int i = l; i <= r; ++ i) a[i] = tmp[i];
  Divide(l, L - 1); Divide(R + 1, r);//这不能二分一个Mid,因为S集和T集的大小不一定相同
}

int main() {
  int T, u, v, c, q;
  scanf("%d", &T);
  while(T --) {
    net.clear();
    scanf("%d%d", &n, &m); net.n = n;
    for(int i = 1; i <= n; ++ i) a[i] = i;
    for(int i = 1; i <= n; ++ i)
      for(int j = 1; j <= n; ++ j) ans[i][j] = inf;
    for(int i = 1; i <= m; ++ i) {
      scanf("%d%d%d", &u, &v, &c);
      net.add(u, v, c);
    }
    Divide(1, n);
    scanf("%d", &q);
    for(int i = 1; i <= q; ++ i) {
      int tmp = 0;
      scanf("%d", &c);
      for(int u = 1; u <= n; ++ u) {
        for(int v = u + 1; v <= n; ++ v) {
          if(ans[u][v] <= c) {
            ++ tmp;
          }
        }
      }
      printf("%d\n", tmp);
    }
    puts("");
  }
  return 0;
}

题目2 CQOI2016 不同的最小割

题目大意:

求所有点对问不同的最小割数目。

算法讨论: 最小割 分治

和上面的一个题有区别么?

代码:

#include <cstdlib>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <set>

using namespace std;
const int N = 850 + 5;
const int M = 8500 + 5;
const int oo = 0x3f3f3f3f;

int n, m;
int ans[N][N], tmp[N], a[N];
bool mark[N];
set <int> lts;

struct Edge {
  int from, to, cap, flow;
  Edge(int u = 0, int v = 0, int cap = 0, int flow = 0):
    from(u), to(v), cap(cap), flow(flow) {}
};

struct Dinic {
  int n, m, s, t;
  int dis[N], cur[N], que[N * 10];
  bool vis[N];
  vector <Edge> edges;
  vector <int> G[N];

  void add(int from, int to, int cap) {
    edges.push_back(Edge(from, to, cap, 0));
    edges.push_back(Edge(to, from, cap, 0));
    m = edges.size();
    G[from].push_back(m - 2);
    G[to].push_back(m - 1);
  }

  bool bfs() {
    int head = 1, tail = 1;
    memset(vis, false, (n + 1) * sizeof (bool));
    dis[s] = 0; vis[s] = true; que[head] = s;
    while(head <= tail) {
      int x = que[head];
      for(int i = 0; i < (signed) G[x].size(); ++ i) {
        Edge &e = edges[G[x][i]];
        if(!vis[e.to] && e.cap > e.flow) {
          vis[e.to] = true;
          dis[e.to] = dis[x] + 1;
          que[++ tail] = e.to;
        }
      }
      ++ head;
    }
    return vis[t];
  }

  int dfs(int x, int a) {
    if(x == t || a == 0) return a;
    int flw = 0, f;
    for(int &i = cur[x]; i < (signed) G[x].size(); ++ i) {
      Edge &e = edges[G[x][i]];
      if(dis[e.to] == dis[x] + 1 && (f = dfs(e.to, min(a, e.cap - e.flow))) > 0) {
        e.flow += f; edges[G[x][i] ^ 1].flow -= f; a -= f; flw += f;
        if(a == 0) break;
      }
    }
    return flw;
  }

  int maxflow(int s, int t) {
    this->s = s; this->t = t;
    int flw = 0;
    while(bfs()) {
      memset(cur, 0, sizeof cur);
      flw += dfs(s, oo);
    }
    return flw;
  }

  void rebuild() {
    for(int i = 0; i < (signed) edges.size(); ++ i)
      edges[i].flow = 0;
  }

  void dfs(int u) {
    mark[u] = true;
    for(int i = 0; i < (signed) G[u].size(); ++ i) {
      Edge e = edges[G[u][i]];
      if(!mark[e.to] && e.cap > e.flow)
        dfs(e.to);
    }
  }
}net;

void Divide(int l, int r) {
  if(l >= r) return;
  net.rebuild();
  int nowflow = net.maxflow(a[l], a[r]);
  memset(mark, false, (n + 1) * sizeof(bool));
  net.dfs(a[l]);
  for(int i = 1; i <= n; ++ i)
    if(mark[i])
      for(int j = 1; j <= n; ++ j)
        if(!mark[j])
          ans[i][j] = min(ans[i][j], nowflow), ans[j][i] = ans[i][j];
  int L = l, R = r;
  for(int i = l; i <= r; ++ i)
    if(mark[a[i]]) tmp[L ++] = a[i];
    else tmp[R --] = a[i];
  for(int i = l; i <= r; ++ i)
    a[i] = tmp[i];
  Divide(l, L - 1); Divide(R + 1, r);
}

int main() {
  int u, v, c;
  scanf("%d%d", &n, &m);
  for(int i = 1; i <= m; ++ i) {
    scanf("%d%d%d", &u, &v, &c);
    net.add(u, v, c);
  }
  for(int i = 1; i <= n; ++ i) a[i] = i;
  for(int i = 1; i <= n; ++ i)
    for(int j = 1; j <= n; ++ j) ans[i][j] = oo;
  net.n = n;
  Divide(1, n);
  for(int i = 1; i <= n; ++ i) {
    for(int j = i + 1; j <= n; ++ j) {
      lts.insert(ans[i][j]);
    }
  }
  printf("%d\n", lts.size());
  return 0;
}
时间: 2024-10-09 01:29:28

网络流题目泛做(费用流的不写在这里面)的相关文章

【BZOJ2597】【Wc2007】剪刀石头布 费用流,没写zkw卡时过

题解,比较常规式是费用流,没写那个神贪心. 首先是三元环需要取补集,先C(n,3)算出总环数,然后减去失败的三元环. 我们发现在一个三元环中,如果有某个选手入度(or 出度)为2,那么就会破坏这个环. 所以当选手有x个度时,就会破坏∑(i∈[0,x])个三元环(0+....+x-1),P.S.这个是C(x,2)推出来的. 建图: 第一层源点 到下一层每个点若干条边 条数: 那个选手可能赢的次数, 费用: 第i条边费用i-1, 流量: 1 第二层选手 到可能赢的每个对应竞赛建边 条数: 1 费用:

最大权闭合子图题目泛做

题目1 POJ2987 题目大意: 一个公司要裁员,每个成员都有自己的效益值,可正可负,而且每个人都有自己的直接下属,如果某个人被裁员,那么他的直接下属,他的下属的下属....都会离开这家公司. 现在请你确定裁员的方案,求最小裁员人数和公司的最大收益. 算法讨论: 选了一个点,其后继都必须要选,这是闭合子图的特点.所以这个题就是裸题啦. 最小裁员人数就是与S相连的点数,公司的最大收益就是正权和-最小割. Code: 1 #include <cstdlib> 2 #include <cst

[网络流]Farm Tour(费用流

Farm Tour 题目描述 When FJ's friends visit him on the farm, he likes to show them around. His farm comprises N (1 <= N <= 1000) fields numbered 1..N, the first of which contains his house and the Nth of which contains the big barn. A total M (1 <= M

[DynamicProgramming]动态规划题目泛做

Educational Codeforces Round 12 F 大意: 求n(n<=1011)以内恰好有4个因数的数的个数 分析: 首先一个数恰好有4个因数,说明它质因数分解之后是两个质数的乘积或是一个质数的三次方,对于后一种情况我们直接n1/3就能算出来,关键在于计算n以内有多少个数是两个素数的乘积. 设n=p1?p2,则必然有p1<n?√,p2>n?√,我们枚举p1,那么问题就在于np1 内有多少个素数. 这一点我们用dp来解决. 记pj为第j个素数,dp[n][j]为[1,n]

小题目泛做

题目1 CodeForces 605 A 题目大意:给定一个长度为n的序列,每次可以把一个数放到开头或者最后,求升序排好的最小次数. 求出原序列位置的最长上升子序列,用n减去最大的.我们只要保证相对大小不变就可以了. Code: 1 #include <cstdio> 2 #include <iostream> 3 #include <algorithm> 4 #include <cstdlib> 5 #include <cstring> 6 7

可持久化数据结构题目泛做。

个人理解: 每个新的线段树的一个结点保存的是1...位置 i中的数字在相应的区间上有几个. 然后我们用r-(l-1)得到的就是l...r上的中字在相应的区间中出现了几个. 题目1 POJ2104 题目大意:静态查询区间第K小值. 裸的可持久化线段树. 1 #include <cstdlib> 2 #include <iostream> 3 #include <algorithm> 4 #include <cstdio> 5 #include <cstr

二分图题目泛做(为了对应陈学姐的课件)

题1:BZOJ1854 SCOI2010游戏 每个属性x,y分别向i连条边,匹配到不能匹配为止. 输出即可. 1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <iostream> 5 #include <algorithm> 6 7 using namespace std; 8 9 const int N = 1000000 + 5; 10 11 i

noi往届题目泛做

noi2015 Day1 t1 程序自动分析  离散化+并查集 t2 软件包管理器  裸树链剖分 t3 寿司晚宴  状压dp Day2 t1 荷马史诗 哈夫曼多叉树 t2 品酒大会 后缀数组按照height排序+并查集 t3 小园丁与老司机 noi2014 Day1 t1 起床困难综合症  按位dp,贪心 t2 魔法森林 link-cut-tree维护MST t3 题答 Day2 t1 动物园 KMP简单题 t2 随机数生成器  贪心,每一行的区间是连续不跨越的,卡内存 t3 购票 树链剖分+线

BZOJ 2245 SDOI 2011 工作安排 费用流

题目大意:有一些商品需要被制造,有一些员工,每一个员工会做一些物品,然而这些员工做物品越多,他们的愤怒值越大,这满足一个分段函数.给出哪些员工可以做哪些东西,给出这些分段函数,求最小的愤怒值以满足需要被制造的商品. 思路:费用流.我写的朴素费用流好像很慢,有时间学一学费用流的多路增广. 由于题目中满足那些分段函数是满足单调递增的性质的,所以就可以如下建图: S->每个人,费用0,流量INF 每个商品->T,费用0,流量为需要改商品的数量 对于每个人虚拟建n个节点(n<=5) 每个人-&g