ZOJ1994有源汇上下界可行流
Posted df-yimeng
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ZOJ1994有源汇上下界可行流相关的知识,希望对你有一定的参考价值。
http://fastvj.rainng.com/contest/236779#problem/G
Description:
n 行 m 列
给你行和 与 列和
然后有Q个限制,表示特定单元格元素大小的范围,最后问你可行的矩阵值
Solution:
有源汇上下界最大流问题,初始源点 连 行和流量是该行对应得行和,然后列连初始汇点,容量为列的列和,详细的限制,对应于行列之间,因为我最后要输出一个矩阵,所以n * m每一条边都要链接最后,手动连一条 t s inf的边,变成无源汇有上下界可行流问题,然后拆边,把对应边的流量,放到数组里,输出就好啦
Code:
前面比较基础的Dinic,数据操作函数,加边操作
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #define inf (1 << 28) using namespace std; const int maxn = 25; const int maxm = 210; const int mn = 505; const int mm = 440020; struct node{ int to,val,pre,lid; }e[mm]; int id[mn],cnt=0; int cur[mn]; int flor[mn]; int upflow[mn]; int lowf[maxm][maxn]; int upf[maxm][maxn]; int out[maxm][maxn]; void init() { memset(id,-1,sizeof(id)); memset(upflow,0,sizeof(upflow)); cnt = 0; } void add(int from,int to,int val,int lid) { e[cnt].to = to; e[cnt].val = val; e[cnt].lid = lid; e[cnt].pre = id[from]; id[from] = cnt++; swap(from,to); e[cnt].to = to; e[cnt].val = 0; e[cnt].lid = lid; e[cnt].pre = id[from]; id[from] = cnt++; } void addflow(int from,int to,int low,int up,int lid) { upflow[from] -= low; upflow[to] += low; add(from,to,up-low,lid); } bool bfs(int s,int t) { memset(flor,0,sizeof(flor)); flor[s] = 1; queue<int> q; while(q.size())q.pop(); q.push(s); while(q.size()) { int now = q.front(); q.pop(); for(int i = id[now];~i;i = e[i].pre) { int to = e[i].to; int val = e[i].val; if(val > 0 && flor[to] == 0) { flor[to] = flor[now] + 1; q.push(to); if(to == t) return true; } } } return false; } int dfs(int s,int t,int value) { if(s == t || value == 0)return value; int ret = value,a; for(int &i = cur[s];~i;i = e[i].pre) { int val = e[i].val; int to = e[i].to; if(flor[to] == flor[s] + 1 && (a = dfs(to,t,min(ret,val)))) { e[i].val -= a; e[i^1].val += a; ret -= a; if(ret == 0)break; } } if(ret == value)flor[s] = 0; return value - ret; } int Dinic(int s,int t) { int ret = 0; while(bfs(s,t)) { memcpy(cur,id,sizeof(id)); ret += dfs(s,t,inf); } return ret; } void addlimit(int i,int j,char op,int lim) { if(op == ‘=‘) { upf[i][j] = lowf[i][j] = lim; } else if(op == ‘>‘) { lowf[i][j] = max(lowf[i][j],lim+1); } else { upf[i][j] = min(upf[i][j],lim-1); } }
然后根据输入一点点的加边,这是行和和列和对应的边
scanf("%d%d",&n,&m); s = 0; t = n + m + 1; ss = t + 1; tt = ss + 1; int lsum; for(int i = 1;i <= n;++i) { scanf("%d",&lsum); addflow(s,i,lsum,lsum,0); } for(int i = 1;i <= m;++i) { scanf("%d",&lsum); addflow(n + i,t,lsum,lsum,0); } int limitnum;
然后根据题目中给出的限制条件,填充上下界数组
scanf("%d",&limitnum); for(int i = 1;i <= n;++i) { for(int j = 1;j <= m;++j) { lowf[i][j] = 0; upf[i][j] = inf; } } int row,col,lim; char op; for(int i = 1;i <= limitnum;++i) { scanf("%d %d %c %d",&row,&col,&op,&lim); if(row == 0 && col == 0) { for(int i = 1;i <= n;++i) { for(int j = 1;j <= m;++j) { addlimit(i,j,op,lim); } } } else if(row == 0) { for(int i = 1;i <= n;++i) { addlimit(i,col,op,lim); } } else if(col == 0) { for(int i = 1;i <= m;++i) { addlimit(row,i,op,lim); } } else { addlimit(row,col,op,lim); } }
根据上下界数组,添加有上下界边
int tot = 0; for(int i = 1;i <= n;++i) { for(int j = 1;j <= m;++j) { addflow(i,n+j,lowf[i][j],upf[i][j],++tot); } }
加边完成后,转化为无源汇有上下界可行流 ———— 循环流
add(t,s,inf,0); int sum = 0; for(int i = s;i <= t;++i) { if(upflow[i] < 0) { add(i,tt,-upflow[i],0); } else { sum += upflow[i]; add(ss,i,upflow[i],0); } }
跑DIinc,如果存在可行流的话
就输出叭~~
for(int now = n+1;now <= n + m;++now) { for(int i = id[now];~i;i = e[i].pre) { int to = e[i].to; int lid = e[i].lid; if(lid == 0 || i % 2 == 0)continue; out[to][now-n] = lowf[to][now-n] + e[i].val; } } for(int i = 1;i <= n;++i) { for(int j = 1;j <= m;++j) { if(j == 1) printf("%d",out[i][j]); else printf(" %d",out[i][j]); } printf(" "); }
完整代码
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #define inf (1 << 28) using namespace std; const int maxn = 25; const int maxm = 210; const int mn = 505; const int mm = 440020; struct node{ int to,val,pre,lid; }e[mm]; int id[mn],cnt=0; int cur[mn]; int flor[mn]; int upflow[mn]; int lowf[maxm][maxn]; int upf[maxm][maxn]; int out[maxm][maxn]; void init() { memset(id,-1,sizeof(id)); memset(upflow,0,sizeof(upflow)); cnt = 0; } void add(int from,int to,int val,int lid) { e[cnt].to = to; e[cnt].val = val; e[cnt].lid = lid; e[cnt].pre = id[from]; id[from] = cnt++; swap(from,to); e[cnt].to = to; e[cnt].val = 0; e[cnt].lid = lid; e[cnt].pre = id[from]; id[from] = cnt++; } void addflow(int from,int to,int low,int up,int lid) { upflow[from] -= low; upflow[to] += low; add(from,to,up-low,lid); } bool bfs(int s,int t) { memset(flor,0,sizeof(flor)); flor[s] = 1; queue<int> q; while(q.size())q.pop(); q.push(s); while(q.size()) { int now = q.front(); q.pop(); for(int i = id[now];~i;i = e[i].pre) { int to = e[i].to; int val = e[i].val; if(val > 0 && flor[to] == 0) { flor[to] = flor[now] + 1; q.push(to); if(to == t) return true; } } } return false; } int dfs(int s,int t,int value) { if(s == t || value == 0)return value; int ret = value,a; for(int &i = cur[s];~i;i = e[i].pre) { int val = e[i].val; int to = e[i].to; if(flor[to] == flor[s] + 1 && (a = dfs(to,t,min(ret,val)))) { e[i].val -= a; e[i^1].val += a; ret -= a; if(ret == 0)break; } } if(ret == value)flor[s] = 0; return value - ret; } int Dinic(int s,int t) { int ret = 0; while(bfs(s,t)) { memcpy(cur,id,sizeof(id)); ret += dfs(s,t,inf); } return ret; } void addlimit(int i,int j,char op,int lim) { if(op == ‘=‘) { upf[i][j] = lowf[i][j] = lim; } else if(op == ‘>‘) { lowf[i][j] = max(lowf[i][j],lim+1); } else { upf[i][j] = min(upf[i][j],lim-1); } } int main() { int T; scanf("%d",&T); int n,m,s,t,ss,tt; while(T--) { init(); scanf("%d%d",&n,&m); s = 0; t = n + m + 1; ss = t + 1; tt = ss + 1; int lsum; for(int i = 1;i <= n;++i) { scanf("%d",&lsum); addflow(s,i,lsum,lsum,0); } for(int i = 1;i <= m;++i) { scanf("%d",&lsum); addflow(n + i,t,lsum,lsum,0); } int limitnum; scanf("%d",&limitnum); for(int i = 1;i <= n;++i) { for(int j = 1;j <= m;++j) { lowf[i][j] = 0; upf[i][j] = inf; } } int row,col,lim; char op; for(int i = 1;i <= limitnum;++i) { scanf("%d %d %c %d",&row,&col,&op,&lim); if(row == 0 && col == 0) { for(int i = 1;i <= n;++i) { for(int j = 1;j <= m;++j) { addlimit(i,j,op,lim); } } } else if(row == 0) { for(int i = 1;i <= n;++i) { addlimit(i,col,op,lim); } } else if(col == 0) { for(int i = 1;i <= m;++i) { addlimit(row,i,op,lim); } } else { addlimit(row,col,op,lim); } } int tot = 0; for(int i = 1;i <= n;++i) { for(int j = 1;j <= m;++j) { addflow(i,n+j,lowf[i][j],upf[i][j],++tot); } } add(t,s,inf,0); int sum = 0; for(int i = s;i <= t;++i) { if(upflow[i] < 0) { add(i,tt,-upflow[i],0); } else { sum += upflow[i]; add(ss,i,upflow[i],0); } } if(Dinic(ss,tt) == sum) { for(int now = n+1;now <= n + m;++now) { for(int i = id[now];~i;i = e[i].pre) { int to = e[i].to; int lid = e[i].lid; if(lid == 0 || i % 2 == 0)continue; out[to][now-n] = lowf[to][now-n] + e[i].val; } } for(int i = 1;i <= n;++i) { for(int j = 1;j <= m;++j) { if(j == 1) printf("%d",out[i][j]); else printf(" %d",out[i][j]); } printf(" "); } } else { printf("IMPOSSIBLE "); } if(T)printf(" "); } return 0; }
以上是关于ZOJ1994有源汇上下界可行流的主要内容,如果未能解决你的问题,请参考以下文章
[zoj] 3229 Shoot the Bullet || 有源汇上下界最大流