网络流
2015-08-22 23:05
701 查看
1.飞行员配对方案
有权二分图最大匹配,KM算法解决 O(n^3)
2.太空飞行计划
选Bi必要条件/前提是选择集合{Aj},每个点有一个权值,求满足依赖关系的最大点权集合。
建图方式为Bi向{Aj}中的每一个点都连一条边,转化为找该图中的最大权闭合图。
闭合图定义:闭合图中边的终点一定在闭合图中(无出边),恰好满足题目的必要条件要求(必要条件属于该集合)
求法:建立网络图:
add(S,u,w) wu>0
add(u,T,-w) wu<0
add(u,v,INF) <u,v>属于原图
最大权闭合图wmax=∑wu>0wu−Maxflow(S,T)
拓展:
1.如果依赖关系存在环,那么可以先对原图缩点,产生新的DAG图,缩点的权值为点内权值和。在新图中采用上述方法求解。
3.DAG图的最小点路径覆盖
最少几条点不相交的路径可以覆盖DAG图
拆点,add(i,j+n,1),<i,j>属于E,求最大二分图匹配
可以用匈牙利算法或者最大流做。
最大流在构造可行解时,需要从大于等于n+1的节点堆中找到第一个起点,即k+n−>2∗n+1的残量不为0。然后再以k为起点遍历寻找路径,寻找的过程是找残量为0的边
hdu4780
题意:给定n个时间区间加工n种方案,共有m台机器进行加工,同一台机器相同时间只加工一种方案。
1.如果某方案的起始时间大于规定时间,那么需要付出额外代价。
2.同一台机器对于加工的第一个方案有一个延迟开始时间和额外代价。
3.不同方案之间转换也有一个延迟时间和额外代价
求加工所有方案所付出的最小额外代价。
解法:经过分析可得:
1.每个方案加工结束的时间固定不变
2.只要确定每个方案前的加工方案,就可以确定该方案需要付出的额外代价
因此为了确定总额外代价,只需要找到每个方案的前驱即可
并且题意满足:如果i方案能够在j方案后进行加工,那么j方案一定不可以在i方案前进行加工!
建图如下:
二分图左侧有n+m个点,表示待定的前驱,右侧有n个点,表示需要确定前驱的点。如果i方案后可以加工j方案/i机器可以首先加工j方案,那么从左向右引边即可。最后建立一个超级源点和超级汇点,求最小费用最大流即可。
uvalive 2531
题意:n支队伍进行比赛,每支队伍进行比赛数目相同,每场比赛一支队伍取胜,一支队伍败。给出每支队伍当前胜场和负场,以及每两个队伍还剩余的比赛场数,求可能获得冠军的队伍。
解法:枚举每支队伍,判断其是否可以获得冠军。判断过程:将m场比赛的胜利分配给n个队伍,每支队伍还可以获胜的次数不超过total-w[i],total为当前枚举队伍的胜场数,w[i]为第i支队伍的当前胜场数,判断是否可以将所有比赛的胜利分配出去。
确定分配关系和个数,且得到个数有上限的分配问题可以建立二分图,求最大流,如果满流,那么全都分配出去了。
uva10779
题意:1个人和另外n个人交换m件物品,交换规则如下:
1.n个人只和他交换,不会互相交换
2.n个人只会用手上重复的物品交换他所没有的物品
问最终他最多可以获得多少种不同的物品?
解法:1-n交换m物品问题,对于每个人,能给他和从他那里得到的是两个m的不相交子集,求能够得到的最大不同物品集合
建图如下:
1-m表示m个物品,m+1-m+n表示n个人
add(S,i,a[1][i]):a[1][j]为第一个人的最初的j物品数量
如果第i个人没有j物品,那么他可以得到1件j物品,否则他可以给出num-1个j物品
最后将1-m连向T,容量为1,表示能够得到的最大不同数量
uva11613
题意:m个月每个月最多可以生产ni件物品,每件生产费用为mi,可以储存ei个月,最多可以卖出si件物品,单价为pi。每件物品储存一个月消耗成本I,求最大利润。
解法:将M个月拆成2*M个点,分别表示买进和卖出,建立二分图+S+T,求解最小费用最大流即可。
uva10806
题意:从s到t再返回s不重复经过同一条边的最短路径
解法:原题可以转化为:最大流为2的最小费用流
建图:S’->S,建立流量为2,费用为0的边,求S’->T的最小费用流即可
拓展:
1.求流量为k的最小费用流:S->S’,建立流量为k,费用为0的边即可
2.从s到t再返回s不重复经过同一点(除s)的最短路径
拆点限制点经过一次,求费用为2的最小费用流即可
uvalive3268
题意:n个人中每个人可以分配到某些组中,但实际上每个人只能分配到一组中去。求组中人员最多的最小值
解法:分配问题,建立二分图,二分组中元素个数即可
uva11167
题意:每个猴子在一个固定时间段[si,ti]内可以喝水,单位时间只喝一单位水,而且必须要喝掉Vi单位水,但是同一个时间点最多有m个猴子同时喝水。输出一种合理的时间分配方案。
解法:分配模型,建立二分图。但是时间取值范围较大,需要离散化。在残余网络中从S出发遍历残量不为0的边,在此段时间内取出一段合法解即可,但是需要合并连续的区间,比较麻烦。
uva11082
题意:已知矩阵每行和每列的和,构造出一组可行解,矩阵中每个值的变化范围是[1,20]
解法:行与列的值分配问题,建立二分图模型。关键点在于行之和=列之和,由此可得到流量平衡,容易建图。需要避免流量为0的情况,技巧在于先把所有值-1,求出结果后再都加上1即可
有权二分图最大匹配,KM算法解决 O(n^3)
2.太空飞行计划
选Bi必要条件/前提是选择集合{Aj},每个点有一个权值,求满足依赖关系的最大点权集合。
建图方式为Bi向{Aj}中的每一个点都连一条边,转化为找该图中的最大权闭合图。
闭合图定义:闭合图中边的终点一定在闭合图中(无出边),恰好满足题目的必要条件要求(必要条件属于该集合)
求法:建立网络图:
add(S,u,w) wu>0
add(u,T,-w) wu<0
add(u,v,INF) <u,v>属于原图
最大权闭合图wmax=∑wu>0wu−Maxflow(S,T)
拓展:
1.如果依赖关系存在环,那么可以先对原图缩点,产生新的DAG图,缩点的权值为点内权值和。在新图中采用上述方法求解。
3.DAG图的最小点路径覆盖
最少几条点不相交的路径可以覆盖DAG图
拆点,add(i,j+n,1),<i,j>属于E,求最大二分图匹配
可以用匈牙利算法或者最大流做。
最大流在构造可行解时,需要从大于等于n+1的节点堆中找到第一个起点,即k+n−>2∗n+1的残量不为0。然后再以k为起点遍历寻找路径,寻找的过程是找残量为0的边
#include<cstdio> #include<algorithm> #include<vector> #include<cmath> #include<string.h> #include<math.h> #include<queue> #include<map> #define N maxn #define ll long long using namespace std; const int INF = 0x3f3f3f3f; const int maxn = 6000+10; int m,n; struct node{ int nx,v,cap,flow; }e[maxn<<2]; int hd[maxn],tt,S,T; void init(){ memset(hd,-1,sizeof(hd)); tt=0; S=0,T=2*n+1; } void add(int u,int v,int cap){ e[tt].v=v,e[tt].nx=hd[u],e[tt].cap=cap,e[tt].flow=0,hd[u]=tt++; } void addedge(int u,int v,int cap){ add(u,v,cap),add(v,u,0); } int d[maxn],used[maxn]; bool BFS(){ queue<int> q; for(int i=S;i<=T;i++) d[i]=-1; q.push(S),d[S]=0; while(!q.empty()){ int u=q.front(); q.pop(); for(int i=hd[u];i!=-1;i=e[i].nx){ int v=e[i].v; 1bddf if(d[v]==-1&&e[i].cap-e[i].flow>0){ d[v]=d[u]+1; q.push(v); } } } return d[T]!=-1; } int DFS(int u,int a){ if(u==T||a==0) return a; int f=0,m; for(int i=used[u];i!=-1;used[u]=i=e[i].nx){ int v=e[i].v; if(d[v]==d[u]+1&&(m=DFS(v,min(a,e[i].cap-e[i].flow)))>0){ e[i].flow+=m,e[i^1].flow-=m; f+=m,a-=m; if(a==0) break; } } return f; } int Maxflow(int S,int T){ int ans=0; while(BFS()){ for(int i=S;i<=T;i++) used[i]=hd[i]; ans+=DFS(S,INF); } return ans; } void dfs(int u){ for(int i=hd[u];i!=-1;i=e[i].nx){ int v=e[i].v; if(e[i].cap==e[i].flow&&v>=n+1&&v<=2*n){//找残量为0的边 printf(" %d",v-n); dfs(v-n); } } } int main(){ //freopen("path3.in","r",stdin); //freopen("path3.out","w",stdout); scanf("%d%d",&n,&m); init(); for(int i=1;i<=n;i++) addedge(S,i,1),addedge(i+n,T,1); for(int i=1;i<=m;i++){ int x,y; scanf("%d%d",&x,&y); addedge(x,y+n,1); } int f=Maxflow(S,T); for(int i=1+n;i<=2*n;i++){ for(int j=hd[i];j!=-1;j=e[j].nx){ int v=e[j].v; if(v==T&&e[j].flow<e[j].cap){//找到路径的所有起点 printf("%d",i-n); dfs(i-n); printf("\n"); } } } printf("%d\n",n-f); return 0; }
hdu4780
题意:给定n个时间区间加工n种方案,共有m台机器进行加工,同一台机器相同时间只加工一种方案。
1.如果某方案的起始时间大于规定时间,那么需要付出额外代价。
2.同一台机器对于加工的第一个方案有一个延迟开始时间和额外代价。
3.不同方案之间转换也有一个延迟时间和额外代价
求加工所有方案所付出的最小额外代价。
解法:经过分析可得:
1.每个方案加工结束的时间固定不变
2.只要确定每个方案前的加工方案,就可以确定该方案需要付出的额外代价
因此为了确定总额外代价,只需要找到每个方案的前驱即可
并且题意满足:如果i方案能够在j方案后进行加工,那么j方案一定不可以在i方案前进行加工!
建图如下:
二分图左侧有n+m个点,表示待定的前驱,右侧有n个点,表示需要确定前驱的点。如果i方案后可以加工j方案/i机器可以首先加工j方案,那么从左向右引边即可。最后建立一个超级源点和超级汇点,求最小费用最大流即可。
#include<bits/stdc++.h> using namespace std; const int INF = 0x3f3f3f3f; const int maxn = 1000; const int maxm = 4000000+10; int n,m,k; int candy_s[maxn],candy_t[maxn]; int c[maxn][maxn],d[maxn][maxn]; int E[maxn][maxn],f[maxn][maxn]; int S,T,ct,mf; struct node{ int u,v,cap,flow,cost,nxt; }e[maxm]; int head[maxn],tot; void read(int a[][maxn],int n,int m){ for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&a[i][j]); } void add(int u,int v,int cap,int cost){ e[tot].u=u,e[tot].v=v,e[tot].cap=cap,e[tot].flow=0,e[tot].cost=cost,e[tot].nxt=head[u],head[u]=tot++; e[tot].u=v,e[tot].v=u,e[tot].cap=0,e[tot].flow=0,e[tot].cost=-cost,e[tot].nxt=head[v],head[v]=tot++; } int p[maxn]; void spfa(int S,int T,int d[]){ bool vis[maxn]; queue<int> q; memset(vis,0,sizeof(vis)); for(int i=S;i<=T;i++) d[i]=INF,p[i]=-1; d[S]=0,q.push(S); while(!q.empty()){ int u=q.front();q.pop(); vis[u]=0; for(int i=head[u];i!=-1;i=e[i].nxt){ int v=e[i].v; if(e[i].cap>e[i].flow&&d[v]>d[u]+e[i].cost){ d[v]=d[u]+e[i].cost,p[v]=i; if(!vis[v]) vis[v]=1,q.push(v); } } } } void mcmf(int S,int T){ ct=mf=0; while(1){ int d[maxn]; spfa(S,T,d); if(d[T]==INF) break; int a=INF; for(int u=p[T];u!=-1;u=p[e[u].u]) a=min(a,e[u].cap-e[u].flow); for(int u=p[T];u!=-1;u=p[e[u].u]) e[u].flow+=a,e[u^1].flow-=a; ct+=d[T]*a,mf+=a; } } int main(){ while(scanf("%d%d%d",&n,&m,&k)!=EOF){ if(n==0&&m==0&&k==0) break; for(int i=1;i<=n;i++) scanf("%d%d",&candy_s[i],&candy_t[i]); read(c,n,m),read(d,n,m),read(E,n,n),read(f,n,n); S=0,T=2*n+m+1; memset(head,-1,sizeof(head)); tot=0; for(int i=1;i<=n;i++){ add(S,i,1,0); for(int j=1;j<=n;j++){ int st=candy_t[i]+E[i][j],cost=f[i][j]; if(i==j||st>=candy_t[j]) continue; if(st>candy_s[j]) cost+=k*(st-candy_s[j]); add(i,n+m+j,1,cost); } for(int j=1;j<=m;j++){ if(c[i][j]>=candy_t[i]) continue; int cost=d[i][j]; if(c[i][j]>candy_s[i]) cost+=k*(c[i][j]-candy_s[i]); add(n+j,i+m+n,1,cost); } add(i+m+n,T,1,0); } for(int i=1;i<=m;i++) add(S,n+i,1,0); mcmf(S,T); if(mf<n) printf("-1\n"); else printf("%d\n",ct); } return 0; }
uvalive 2531
题意:n支队伍进行比赛,每支队伍进行比赛数目相同,每场比赛一支队伍取胜,一支队伍败。给出每支队伍当前胜场和负场,以及每两个队伍还剩余的比赛场数,求可能获得冠军的队伍。
解法:枚举每支队伍,判断其是否可以获得冠军。判断过程:将m场比赛的胜利分配给n个队伍,每支队伍还可以获胜的次数不超过total-w[i],total为当前枚举队伍的胜场数,w[i]为第i支队伍的当前胜场数,判断是否可以将所有比赛的胜利分配出去。
确定分配关系和个数,且得到个数有上限的分配问题可以建立二分图,求最大流,如果满流,那么全都分配出去了。
#include<bits/stdc++.h> using namespace std; const int INF = 0x3f3f3f3f; const int maxn = 1000+10; const int maxm = 1000000+10; int t,n; struct node{ int v,nxt,cap,flow; }e[maxm]; int head[maxn],tot; int w[maxn],def[maxn],a[maxn][maxn],b[maxn][maxn],total,tmp,S,T; int ans[maxn],cnt; void add(int u,int v,int cap){ e[tot].v=v,e[tot].nxt=head[u],e[tot].cap=cap,e[tot].flow=0,head[u]=tot++; e[tot].v=u,e[tot].nxt=head[v],e[tot].cap=0,e[tot].flow=0,head[v]=tot++; } void update(int k){ total=w[k]; for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++){ if(i==k||j==k){ total+=a[i][j]; b[i][j]=0; } else b[i][j]=a[i][j]; } } void build(int k){ S=0,T=n+1,tmp=0; for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++){ if(b[i][j]){ tmp+=b[i][j]; add(S,T,b[i][j]); add(T,i,INF),add(T,j,INF); T++; } } for(int i=1;i<=n;i++){ if(i!=k) add(i,T,total-w[i]); } } int d[maxn],used[maxn]; bool BFS(){ queue<int> q; for(int i=S;i<=T;i++) d[i]=-1; q.push(S),d[S]=0; while(!q.empty()){ int u=q.front(); q.pop(); for(int i=head[u];i!=-1;i=e[i].nxt){ int v=e[i].v; if(d[v]==-1&&e[i].cap-e[i].flow>0){ d[v]=d[u]+1; q.push(v); } } } return d[T]!=-1; } int DFS(int u,int a){ if(u==T||a==0) return a; int f=0,m; for(int i=used[u];i!=-1;used[u]=i=e[i].nxt){ int v=e[i].v; if(d[v]==d[u]+1&&(m=DFS(v,min(a,e[i].cap-e[i].flow)))>0){ e[i].flow+=m,e[i^1].flow-=m; f+=m,a-=m; if(a==0) break; } } return f; } int maxflow(int S,int T){ int ans=0; while(BFS()){ for(int i=S;i<=T;i++) used[i]=head[i]; ans+=DFS(S,INF); } return ans; } int main(){ //freopen("a.txt","r",stdin); scanf("%d",&t); while(t--){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d",&w[i],&def[i]); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&a[i][j]); cnt=0; for(int k=1;k<=n;k++){ update(k); bool flag=false; for(int i=1;i<=n;i++) if(total<w[i]) { flag=true; break; } if(flag) continue; memset(head,-1,sizeof(head)); tot=0; build(k); int mf=maxflow(S,T); if(mf==tmp) ans[cnt++]=k; } for(int i=0;i<cnt;i++){ if(i==cnt-1) printf("%d\n",ans[i]); else printf("%d ",ans[i]); } } return 0; }
uva10779
题意:1个人和另外n个人交换m件物品,交换规则如下:
1.n个人只和他交换,不会互相交换
2.n个人只会用手上重复的物品交换他所没有的物品
问最终他最多可以获得多少种不同的物品?
解法:1-n交换m物品问题,对于每个人,能给他和从他那里得到的是两个m的不相交子集,求能够得到的最大不同物品集合
建图如下:
1-m表示m个物品,m+1-m+n表示n个人
add(S,i,a[1][i]):a[1][j]为第一个人的最初的j物品数量
如果第i个人没有j物品,那么他可以得到1件j物品,否则他可以给出num-1个j物品
最后将1-m连向T,容量为1,表示能够得到的最大不同数量
#include<bits/stdc++.h> using namespace std; const int INF = 0x3f3f3f3f; const int maxn = 1000+10; const int maxk = 100; const int maxm = 1000000+10; int t,n,m; int a[maxn][maxn]; int S,T; struct node{ int v,nxt,cap,flow; }e[maxm]; int head[maxn],tot; int change[maxk][maxk]; void add(int u,int v,int cap){ e[tot].v=v,e[tot].nxt=head[u],e[tot].cap=cap,e[tot].flow=0,head[u]=tot++; e[tot].v=u,e[tot].nxt=head[v],e[tot].cap=0,e[tot].flow=0,head[v]=tot++; } int d[maxn],used[maxn]; bool BFS(){ queue<int> q; for(int i=S;i<=T;i++) d[i]=-1; d[S]=0,q.push(S); while(!q.empty()){ int u=q.front(); q.pop(); for(int i=head[u];i!=-1;i=e[i].nxt){ int v=e[i].v; if(d[v]==-1&&e[i].cap-e[i].flow>0){ d[v]=d[u]+1; q.push(v); } } } return d[T]!=-1; } int DFS(int u,int a){ if(u==T||a==0) return a; int f=0,m; for(int i=used[u];i!=-1;used[u]=i=e[i].nxt){ int v=e[i].v; if(d[v]==d[u]+1&&(m=DFS(v,min(a,e[i].cap-e[i].flow)))>0){ e[i].flow+=m,e[i^1].flow-=m; f+=m,a-=m; if(!a) break; } } return f; } int maxflow(int S,int T){ int ans=0; while(BFS()){ for(int i=S;i<=T;i++) used[i]=head[i]; ans+=DFS(S,INF); } return ans; } int main(){ //freopen("a.txt","r",stdin); scanf("%d",&t); int cas=0; while(t--){ scanf("%d%d",&n,&m); memset(a,0,sizeof(a)); for(int i=1;i<=n;i++){ int num,x; scanf("%d",&num); while(num--){ scanf("%d",&x); a[i][x]++; } } S=0,T=m+n; memset(head,-1,sizeof(head));tot=0; for(int i=1;i<=m;i++){ add(S,i,a[1][i]); for(int j=2;j<=n;j++){ if(!a[j][i]) add(i,j+m-1,1); else if(a[j][i]>1) add(j+m-1,i,a[j][i]-1); } add(i,T,1); } printf("Case #%d: %d\n",++cas,maxflow(S,T)); } return 0; }
uva11613
题意:m个月每个月最多可以生产ni件物品,每件生产费用为mi,可以储存ei个月,最多可以卖出si件物品,单价为pi。每件物品储存一个月消耗成本I,求最大利润。
解法:将M个月拆成2*M个点,分别表示买进和卖出,建立二分图+S+T,求解最小费用最大流即可。
#include<bits/stdc++.h> using namespace std; #define ll long long const ll INF=0x3FFFFFFFFFFFFFFFLL; const ll maxn = 200+10; const ll maxm = 10000+10; ll t; ll S,T; ll M,I,m,n,P,s,E; struct node{ ll u,v,nxt,cap,flow,cost; }e[maxm<<2]; ll head[maxn],tot,p[maxn]; void add(ll u,ll v,ll cap,ll cost){ e[tot].u=u,e[tot].v=v,e[tot].nxt=head[u],e[tot].cap=cap,e[tot].flow=0,e[tot].cost=cost,head[u]=tot++; e[tot].u=v,e[tot].v=u,e[tot].nxt=head[v],e[tot].cap=0,e[tot].flow=0,e[tot].cost=-cost,head[v]=tot++; } void spfa(ll S,ll T,ll d[]){ bool vis[maxn]; queue<int> q; memset(vis,0,sizeof(vis)); for(ll i=S;i<=T;i++) d[i]=INF,p[i]=-1; d[S]=0; q.push(S); while(!q.empty()){ int u=q.front(); q.pop(); vis[u]=0; for(ll i=head[u];i!=-1;i=e[i].nxt){ ll v=e[i].v; if(e[i].cap>e[i].flow&&d[v]>d[u]+e[i].cost){ d[v]=d[u]+e[i].cost,p[v]=i; if(!vis[v]) q.push(v),vis[v]=1; } } } } ll mcmf(ll S,ll T){ ll mf=0,mc=0; while(1){ ll d[maxn]; spfa(S,T,d); if(d[T]==INF) break; if(d[T]>=0) break; ll a=INF; for(ll i=p[T];i!=-1;i=p[e[i].u]) a=min(a,e[i].cap-e[i].flow); for(ll i=p[T];i!=-1;i=p[e[i].u]) e[i].flow+=a,e[i^1].flow-=a; mf+=a,mc+=a*d[T]; } return mc; } int main(){ //freopen("a.txt","r",stdin); scanf("%lld",&t); ll cas=0; while(t--){ scanf("%lld%lld",&M,&I); S=0,T=2*M+1; memset(head,-1,sizeof(head)); tot=0; for(ll i=1;i<=M;i++){ scanf("%lld%lld%lld%lld%lld",&m,&n,&P,&s,&E); add(S,i,n,m); for(ll j=0;j<=E;j++){ if(j+i>M) break; add(i,j+i+M,INF,j*I); } add(i+M,T,s,-P); } printf("Case %lld: %lld\n",++cas,-mcmf(S,T)); } return 0; }
uva10806
题意:从s到t再返回s不重复经过同一条边的最短路径
解法:原题可以转化为:最大流为2的最小费用流
建图:S’->S,建立流量为2,费用为0的边,求S’->T的最小费用流即可
拓展:
1.求流量为k的最小费用流:S->S’,建立流量为k,费用为0的边即可
2.从s到t再返回s不重复经过同一点(除s)的最短路径
拆点限制点经过一次,求费用为2的最小费用流即可
#include<bits/stdc++.h> using namespace std; const int INF = 0x3f3f3f3f; const int maxn = 2000+10; const int maxm = 100000+10; int n,m; struct node{ int u,v,nxt,cap,flow,cost; }e[maxm<<2]; int head[maxn],tot,p[maxn]; int mc,mf; void add(int u,int v,int cap,int cost){ e[tot].u=u,e[tot].v=v,e[tot].nxt=head[u],e[tot].cap=cap,e[tot].flow=0,e[tot].cost=cost,head[u]=tot++; e[tot].u=v,e[tot].v=u,e[tot].nxt=head[v],e[tot].cap=0,e[tot].flow=0,e[tot].cost=-cost,head[v]=tot++; } void spfa(int S,int T,int d[]){ queue<int> q; bool vis[maxn]; memset(vis,0,sizeof(vis)); for(int i=S;i<=T;i++) d[i]=INF,p[i]=-1; d[S]=0; q.push(S); while(!q.empty()){ int u=q.front(); q.pop(); vis[u]=0;//!! for(int i=head[u];i!=-1;i=e[i].nxt){ int v=e[i].v; if(e[i].cap>e[i].flow&&d[v]>d[u]+e[i].cost){ d[v]=d[u]+e[i].cost,p[v]=i;//!! if(!vis[v]) vis[v]=1,q.push(v); } } } } void mcmf(int S,int T){ mc=mf=0; while(1){ int d[maxn]; spfa(S,T,d); if(d[T]==INF) break; int a=INF; for(int i=p[T];i!=-1;i=p[e[i].u]) a=min(a,e[i].cap-e[i].flow); for(int i=p[T];i!=-1;i=p[e[i].u]) e[i].flow+=a,e[i^1].flow-=a; mf+=a,mc+=d[T]*a; } } int main(){ //freopen("a.txt","r",stdin); while(scanf("%d",&n)!=EOF){ if(!n) break; scanf("%d",&m); memset(head,-1,sizeof(head)); tot=0; add(0,1,2,0); while(m--){ int x,y,z; scanf("%d%d%d",&x,&y,&z); add(x,y,1,z); add(y,x,1,z); } mcmf(0,n); if(mf>=2) printf("%d\n",mc); else printf("Back to jail\n"); } return 0; }
uvalive3268
题意:n个人中每个人可以分配到某些组中,但实际上每个人只能分配到一组中去。求组中人员最多的最小值
解法:分配问题,建立二分图,二分组中元素个数即可
uva11167
题意:每个猴子在一个固定时间段[si,ti]内可以喝水,单位时间只喝一单位水,而且必须要喝掉Vi单位水,但是同一个时间点最多有m个猴子同时喝水。输出一种合理的时间分配方案。
解法:分配模型,建立二分图。但是时间取值范围较大,需要离散化。在残余网络中从S出发遍历残量不为0的边,在此段时间内取出一段合法解即可,但是需要合并连续的区间,比较麻烦。
#include<bits/stdc++.h> using namespace std; int n,m; const int INF = 0x3f3f3f3f; const int maxn = 50100+10; const int maxm = 5000000+10; struct node{ int v,nxt,cap,flow; }e[maxm<<1]; int head[maxn],tot,vis[maxn]; int S,T,sum; int l[maxn],r[maxn],v[maxn],a[maxn],b[maxn],tim[maxn],id[maxn],cnt; int now[maxn]; struct Tim{ int st,ed; }ans[maxn],tans[maxn]; bool cmp(Tim a,Tim b){ return a.st<b.st; } void add(int u,int v,int cap){ e[tot].v=v,e[tot].nxt=head[u],e[tot].cap=cap,e[tot].flow=0,head[u]=tot++; e[tot].v=u,e[tot].nxt=head[v],e[tot].cap=0,e[tot].flow=0,head[v]=tot++; } int used[maxn],d[maxn]; bool BFS(){ queue<int> q; for(int i=S;i<=T;i++) d[i]=-1; d[S]=0,q.push(S); while(!q.empty()){ int u=q.front(); q.pop(); for(int i=head[u];i!=-1;i=e[i].nxt){ int v=e[i].v; if(d[v]==-1&&e[i].cap>e[i].flow){ d[v]=d[u]+1; q.push(v); } } } return d[T]!=-1; } int DFS(int u,int a){ if(u==T||a==0) return a; int f=0,m; for(int i=head[u];i!=-1;i=e[i].nxt){ int v=e[i].v; if(d[v]==d[u]+1&&(m=DFS(v,min(a,e[i].cap-e[i].flow)))>0){ e[i].flow+=m,e[i^1].flow-=m; f+=m,a-=m; if(a==0) break; } } return f; } int mf(int S,int T){ int ans=0; while(BFS()){ for(int i=S;i<=T;i++) used[i]=head[i]; ans+=DFS(S,INF); } return ans; } void solve(){ for(int u=1;u<=n;u++){//记录合法区间 int cnt2=0,cnt3=0; for(int i=head[u];i!=-1;i=e[i].nxt){ int v=e[i].v,f=e[i].flow; if(e[i].flow<=0) continue; if(now[v]+f>tim[v-n-1+1]){ int tmp=tim[v-n-1+1]-now[v]; if(tmp) ans[cnt2].st=now[v],ans[cnt2++].ed=tim[v-n-1+1]; ans[cnt2].st=tim[v-n-1],ans[cnt2++].ed=tim[v-n-1]+f-tmp; now[v]=tim[v-n-1]+f-tmp; } else{ ans[cnt2].st=now[v],ans[cnt2++].ed=now[v]+f; now[v]+=f; } } sort(ans,ans+cnt2,cmp); for(int i=0;i<cnt2;){// 合并区间 int j=i; while(j+1<cnt2&&ans[j+1].st==ans[j].ed) j++; tans[cnt3].st=ans[i].st,tans[cnt3++].ed=ans[j].ed; i=j+1; } printf("%d",cnt3); for(int i=0;i<cnt3;i++) printf(" (%d,%d)",tans[i].st,tans[i].ed); printf("\n"); } } int main(){ int cas=0; //freopen("a.txt","r",stdin); while(scanf("%d",&n)!=EOF){ if(!n) break; scanf("%d",&m); S=0; memset(head,-1,sizeof(head)); tot=0,cnt=0,sum=0; memset(vis,0,sizeof(vis)); for(int i=1;i<=n;i++){ scanf("%d%d%d",&v[i],&a[i],&b[i]); sum+=v[i]; if(!vis[a[i]]) tim[cnt++]=a[i],vis[a[i]]=1; if(!vis[b[i]]) tim[cnt++]=b[i],vis[b[i]]=1; } sort(tim,tim+cnt); T=n+cnt+2; for(int i=0;i<cnt;i++){ id[tim[i]]=i; now[i+n+1]=tim[i]; if(i!=cnt-1) add(i+n+1,T,m*(tim[i+1]-tim[i])); } for(int i=1;i<=n;i++){ int l=id[a[i]],r=id[b[i]]; //printf("%d %d\n",l,r); add(S,i,v[i]); for(int j=l;j<=r-1;j++){ //printf("%d %d\n",i,j+n+1); add(i,j+n+1,tim[j+1]-tim[j]); } } int tmp=mf(S,T); //printf("%d %d\n",tmp,sum); if(tmp==sum) { printf("Case %d: Yes\n",++cas); solve(); } else printf("Case %d: No\n",++cas); } return 0; }
uva11082
题意:已知矩阵每行和每列的和,构造出一组可行解,矩阵中每个值的变化范围是[1,20]
解法:行与列的值分配问题,建立二分图模型。关键点在于行之和=列之和,由此可得到流量平衡,容易建图。需要避免流量为0的情况,技巧在于先把所有值-1,求出结果后再都加上1即可
#include<bits/stdc++.h> using namespace std; int n,m; const int INF = 0x3f3f3f3f; const int maxn = 50100+10; const int maxm = 5000000+10; struct node{ int v,nxt,cap,flow; }e[maxm<<1]; int head[maxn],tot,vis[maxn]; int S,T,sum; int l[maxn],r[maxn],v[maxn],a[maxn],b[maxn],tim[maxn],id[maxn],cnt; int now[maxn]; struct Tim{ int st,ed; }ans[maxn],tans[maxn]; bool cmp(Tim a,Tim b){ return a.st<b.st; } void add(int u,int v,int cap){ e[tot].v=v,e[tot].nxt=head[u],e[tot].cap=cap,e[tot].flow=0,head[u]=tot++; e[tot].v=u,e[tot].nxt=head[v],e[tot].cap=0,e[tot].flow=0,head[v]=tot++; } int used[maxn],d[maxn]; bool BFS(){ queue<int> q; for(int i=S;i<=T;i++) d[i]=-1; d[S]=0,q.push(S); while(!q.empty()){ int u=q.front(); q.pop(); for(int i=head[u];i!=-1;i=e[i].nxt){ int v=e[i].v; if(d[v]==-1&&e[i].cap>e[i].flow){ d[v]=d[u]+1; q.push(v); } } } return d[T]!=-1; } int DFS(int u,int a){ if(u==T||a==0) return a; int f=0,m; for(int i=head[u];i!=-1;i=e[i].nxt){ int v=e[i].v; if(d[v]==d[u]+1&&(m=DFS(v,min(a,e[i].cap-e[i].flow)))>0){ e[i].flow+=m,e[i^1].flow-=m; f+=m,a-=m; if(a==0) break; } } return f; } int mf(int S,int T){ int ans=0; while(BFS()){ for(int i=S;i<=T;i++) used[i]=head[i]; ans+=DFS(S,INF); } return ans; } void solve(){ for(int u=1;u<=n;u++){ int cnt2=0,cnt3=0; for(int i=head[u];i!=-1;i=e[i].nxt){ int v=e[i].v,f=e[i].flow; if(e[i].flow<=0) continue; if(now[v]+f>tim[v-n-1+1]){ int tmp=tim[v-n-1+1]-now[v]; if(tmp) ans[cnt2].st=now[v],ans[cnt2++].ed=tim[v-n-1+1]; ans[cnt2].st=tim[v-n-1],ans[cnt2++].ed=tim[v-n-1]+f-tmp; now[v]=tim[v-n-1]+f-tmp; } else{ ans[cnt2].st=now[v],ans[cnt2++].ed=now[v]+f; now[v]+=f; } } sort(ans,ans+cnt2,cmp); for(int i=0;i<cnt2;){ int j=i; while(j+1<cnt2&&ans[j+1].st==ans[j].ed) j++; tans[cnt3].st=ans[i].st,tans[cnt3++].ed=ans[j].ed; i=j+1; } printf("%d",cnt3); for(int i=0;i<cnt3;i++) printf(" (%d,%d)",tans[i].st,tans[i].ed); printf("\n"); } } int main(){ int cas=0; //freopen("a.txt","r",stdin); while(scanf("%d",&n)!=EOF){ if(!n) break; scanf("%d",&m); S=0; memset(head,-1,sizeof(head)); tot=0,cnt=0,sum=0; memset(vis,0,sizeof(vis)); for(int i=1;i<=n;i++){ scanf("%d%d%d",&v[i],&a[i],&b[i]); sum+=v[i]; if(!vis[a[i]]) tim[cnt++]=a[i],vis[a[i]]=1; if(!vis[b[i]]) tim[cnt++]=b[i],vis[b[i]]=1; } sort(tim,tim+cnt); T=n+cnt+2; for(int i=0;i<cnt;i++){ id[tim[i]]=i; now[i+n+1]=tim[i]; if(i!=cnt-1) add(i+n+1,T,m*(tim[i+1]-tim[i])); } for(int i=1;i<=n;i++){ int l=id[a[i]],r=id[b[i]]; add(S,i,v[i]); for(int j=l;j<=r-1;j++){ add(i,j+n+1,tim[j+1]-tim[j]); } } int tmp=mf(S,T); if(tmp==sum) { printf("Case %d: Yes\n",++cas); solve(); } else printf("Case %d: No\n",++cas); } return 0; }
相关文章推荐
- android-async-http异步出现的问题
- Azure SLB + httpd + ILB + HAProxy + Atlas + MySQL
- TCP和UDP的区别
- socket与http的区别
- Socket 通信原理(Android客户端和服务器以TCP&&UDP方式互通)
- tcp和udp
- Unix网络编程开篇
- Netty5中使用LineBasedFrameDecoder解决TCP粘包问题
- 基于递归神经网络的人脸识别探究
- MFC基于socket的网络聊天室的实现
- 初学三种神经网络(前馈,竞争,递归联想存储网络)
- 学习tcl的几个好网络连接
- iOS开发——网络篇——JSON和XML,NSJSONSerialization ,NSXMLParser(XML解析器),NSXMLParserDelegate,MJExtension (字典转模型),GDataXML(三方框架解析XML)
- https实现的几个问题
- Python3.X 抓取网络资源
- hdu1532网络流
- virtualBox 上centos的网络配置 桥接方式(bridge)
- 为什么有了可靠地TCP还需要不可靠的UDP
- UNIX域套接字及TCP、UDP示例
- redis网络超时问题分析