网络流与线性规划24题

来源:互联网 发布:c语言入门小程序 编辑:程序博客网 时间:2024/06/01 09:56

留坑待填


网络流做题技巧:

1、板子很熟的情况下肉眼差错,放弃单步调试。

2、反复检查自己的网络流模型的正确性,确保正确才开始写。

3、手动构造多组数据卡自己的程序,网络流的题目通常样例很垃圾而且对拍不好写。


通用模板

最大流模板:

#include <iostream>#include <cstdio>#include <cstring>#include <queue>#include <vector>#define N 10050#define INF 1<<30using namespace std;struct Node{int u,cap,rec; };vector<Node> e[N];int p[N],S,T;void add_Node(int a,int b,int v) {Node tmp;tmp.u = b;tmp.cap = v;tmp.rec = e[b].size();e[a].push_back(tmp);tmp.u = a;tmp.cap = 0;tmp.rec = e[a].size() - 1;e[b].push_back(tmp);return ;}bool BFS() {bool flag = false;memset(p,0,sizeof(p));queue<int> q;p[S] = 1;while (!q.empty()) {int u = q.front(); q.pop();if (u == T) flag = true;for (int i=0;i<e[u].size();i++) {int v = e[u][i].u;int cp = e[u][i].cap;if (cp > 0 && p[v] == 0) {p[v] = p[u] + 1;q.push(v);}}}return flag;}int DFS(int u,int flow) {if (u == T) return flow;int f = flow , g = 0;for (int i=0;i<e[u].size();i++) {int v = e[u][i].u;int cp = e[u][i].cap;int tmp = 0;if (cp > 0 && p[v] == p[u] + 1 && (tmp = DFS(v,min(f,cp))) > 0) {f -= tmp;g += tmp;e[u][i].cap -= tmp;int rc = e[u][i].rec;e[v][rc].cap += tmp;}}return g;}void build() {}int main(){build();int max_flow = 0;while (BFS()) max_flow += DFS(S,INF);int ans = max_flow;printf("%d\n",ans);return 0;}



24题建图

第一题:飞行员配对方案(二分图最大匹配)

建图:直接二分图建图,S连向所有皇家飞行员流量为INF,所有外国飞行员连向T流量为INF,每个皇家飞行员连向外国飞行员流量为1。最大流,最后输出所有的满流边的

void build() {while (1){scanf("%d%d",&x,&y);if (x == y && y == -1) break;if (x>y) swap(x,y);add_Node(x,y,1);}}


第二题:太空飞行计划问题(最大权闭合子图)

建图:超级源连向所有正权点,流量为权值,负权点连向超级汇,流量为权值(取绝对值),对所有的正权和负权的限制,从正权点连向负权点一条边,流量为INF(这条边不会被剪掉)。跑最小割,答案 = 正权点权值和 - 最小割。

思路:剪掉一个正权点即为放弃一个收益,剪掉一个负权点即为放弃一个限制。

void build() {scanf("%d%d",&m,&n);S = ++cnt; T = ++cnt;for (int i=1;i<=m;i++) l[i] = ++cnt;for (int i=1;i<=n;i++) r[i] = ++cnt;for (int i=1;i<=m;i++) {scanf("%d",&v);ans += v;add_Node(S,l[i],v);char ch = ' ';while (ch != '\n') {scanf("%d",&t); ch = getchar();add_Node(l[i],r[t],INF);}}for (int i=1;i<=n;i++) {scanf("%d",&v); add_Node(r[i],T,v);}}


第三题:最小路径覆盖问题(有向无环图最小路径覆盖)

建图:将所有的点拆成两个点<xi,yi>,S连向所有的xi容量为1,所有的yi连向T容量为1,对于原图中的每一条有向边E(u,v),在建图中加入<xu,yv>边,容量为1。答案等于点数-最大流。

思路:xi相当于一个点的出度点,yi相当于一个点的入度点。在最小路径覆盖问题中,一个点的出入度只能为0/1,所以它们连向超级源和超级汇的容量都为1。没有一条连向x,y的漫流边即为在路径覆盖中x,y被连接了。


void build() {S = ++cnt; T = ++cnt;for (int i=1;i<=n;i++) x[i] = ++cnt;for (int i=1;i<=n;i++) add_Node(S,x[i],1);for (int i=1;i<=n;i++) y[i] = ++cnt;for (int i=1;i<=n;i++) add_Node(y[i],T,1);for (int i=1;i<=m;i++) {int u = x[ e[i].a ]  , v = y[ e[i].y ];add_Node(u,v,1);}}


第四题:魔术球问题(有向无环图最小路径覆盖)

建图:二分最大值,将所有的球拆点成xi,yi,分别连向超级源和超级汇,容量为1,对于球a,b(a<b且a+b=k^2),连接边<xa,yb>容量为1,若点数-最大流<=柱子数目,则判定为true.

思路:最小路径覆盖问题

void build() {S = ++cnt; T = ++cnt;for (int i=1;i<=n;i++) x[i] = ++cnt;for (int i=1;i<=n;i++) add_Node(S,x[i],1);for (int i=1;i<=n;i++) y[i] = ++cnt;for (int i=1;i<=n;i++) add_Node(y[i],T,1);for (int i=1;i<=n;i++)for (int j=1;j<=n;j++)if (floor( sqrt(i+j) ) == sqrt(i+j)) add_Node(i,j,1);}

第五题:圆桌问题(二分图多重匹配)

建图:

S连向所有的单位,容量为该单位人数。

单位连向所有的餐桌,容量为1。

所有的餐桌连向T,容量为餐桌的容量。

最大流。输出所有满流的<单位、餐桌>边。

void build() {scanf("%d%d",&n,&m);S = ++cnt; T = ++cnt;for (int i=1;i<=n;i++) {scanf("%d",&t);l[i] = ++cnt;add_Node(S,l[i],t);}for (int i=1;i<=m;i++) {scanf("%d",&t);r[i] = ++cnt;add_Node(r[i],T,t);}for (int i=1;i<=n;i++)for (int j=1;j<=m;j++) add_Node(l[i],r[j],1);}

第六题:最长递增子序列问题(最大不相交路径)

诶一开始建的图是错的,少考虑了很多东西

第一问第二问DP,第三问网络流(需要借助前两问的dp数组)

令F[i]为最长的以i结尾的LIS的长度

建图:

对所有的点拆成xi,yi,且加入边xi,yi,如果是两端的点则容量为INF否则容量为1

对所有的F[i] = 1,S连向xi容量为1

对所有的F[i] = 第一问答案,yi连向T容量为1

对于每一对$ <i,j> $满足$ F[i]+1 = F[j] $ , $i<j$ 且 $ a[i] < a[j] $,建有向边<yi,xj>容量为1。

最大流。

void build() {for (int i=1;i<=n;i++)for (int j=i+1;j<=n;j++)if (F[i]+1 == F[j]) add_Node(i,j,1);}



1-6题完成时间:2016年12月10日-2016年12月13日




0 0