网络流汇总....
1 Poj 1149(最大流建图
题目:有m个猪圈,n个顾客,n个顾客依次到达,每个顾客可以打开若干个猪圈,可以选择给当前顾客卖不超过b头猪,并且当前打开的猪圈中的猪可以任意调整。问最多能卖多少猪。
思路:首先是直观的建图,由于有顺序关系,所以考虑给每个顾客建一层图,那么节点数就是n×m,这个数目太大。可以注意到原图中有很多OO权的边,比如可以相互转移的猪圈之间的边。这些边是可以化简的。具体化简思路见http://ycool.com/post/zhhrrm6#pic1
化简之后的图即为:原点向每个猪圈的第一个顾客连边,流量为猪圈里猪的数目。每个顾客向下一个到达这个猪圈的顾客连无穷的边。顾客向汇点连边。
理解:首先这么建图体现了顺序性,每个顾客的可选的猪的数目与前一个到猪圈的顾客选了多少有关。分配方面,每个顾客都和所有的来源连边了,并且流过来的流量是前面决策之后的。
化简的重点是来源,去向。后到的顾客可选的是前面的顾客选剩下的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * @author: Cwind */ ///#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define IOS std::ios::sync_with_stdio (false);std::cin.tie(0) #define pb push_back #define PB pop_back #define bk back() #define fs first #define se second #define sq(x) (x)*(x) #define eps (1e-6) #define IINF (1<<29) #define LINF (1ll<<59) #define INF (1000000000) #define FINF (1e3) #define clr(x) memset((x),0,sizeof (x)); typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> pii; typedef pair<ll,ll> P; const int maxv=100000; struct EDGE{ int to,cap,rev; EDGE(int t,int c,int r):to(t),cap(c),rev(r){} }; vector<EDGE> G[maxv]; void addedge(int from,int to,int cap){///加边 G[from].pb(EDGE(to,cap,G[to].size())); G[to].pb(EDGE(from,0,G[from].size()-1)); } int s=maxv-1; int t=maxv-2; int level[maxv]; queue<int> Q; void bfs(int s){////bfs出分层图 memset(level,-1,sizeof level); level[s]=0; Q.push(s); while(!Q.empty()){ int v=Q.front();Q.pop(); for(int i=0;i<G[v].size();i++){ EDGE &e=G[v][i]; if(e.cap>0&&level[e.to]==-1){ level[e.to]=level[v]+1; Q.push(e.to); } } } } int iter[maxv]; int dfs(int v,int t,int f){///dfs寻找增广路径 if(v==t) return f; for(int &i=iter[v];i<G[v].size();i++){ EDGE &e=G[v][i]; if(e.cap>0&&level[e.to]>level[v]){ int d=dfs(e.to,t,min(f,e.cap)); if(d>0){ e.cap-=d; G[e.to][e.rev].cap+=d; return d; } } } return 0; } int dinic(int s,int t){///dinic算法求解最大流 int flow=0; for(;;){ bfs(s); if(level[t]==-1) return flow; memset(iter,0,sizeof iter); int f; while((f=dfs(s,t,IINF))>0) flow+=f; } return 0; } const int maxn=2000; vector<int> cus[maxn]; int pig[maxn]; int need[maxn]; int n,m; void build(){ for(int i=1;i<=m;i++){ if(cus[i].size()){ addedge(s,cus[i][0],pig[i]); } for(int j=1;j<cus[i].size();j++){ addedge(cus[i][j-1],cus[i][j],INF); } } for(int i=1;i<=n;i++){ addedge(i,t,need[i]); } } int main(){ freopen("/home/slyfc/CppFiles/in","r",stdin); cin>>m>>n; for(int i=1;i<=m;i++){ scanf("%d",&pig[i]); } for(int i=1;i<=n;i++){ int a; scanf("%d",&a); for(int j=0;j<a;j++){ int k; scanf("%d",&k); cus[k].pb(i); } scanf("%d",&need[i]); } build(); printf("%d",dinic(s,t)); return 0; }
2 POJ 1637(混合图欧拉回路判定
题目:给出若干有向或无向边,问是否有欧拉回路。
思路:见http://www.cnblogs.com/kuangbin/p/3537525.html
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * @author: Cwind */ ///#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define IOS std::ios::sync_with_stdio (false);std::cin.tie(0) #define pb push_back #define PB pop_back #define bk back() #define fs first #define se second #define sq(x) (x)*(x) #define eps (1e-6) #define IINF (1<<29) #define LINF (1ll<<59) #define INF (1000000000) #define FINF (1e3) #define clr(x) memset((x),0,sizeof (x)); typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> pii; typedef pair<ll,ll> P; const int maxv=100000; struct EDGE{ int to,cap,rev; EDGE(int t,int c,int r):to(t),cap(c),rev(r){} }; vector<EDGE> G[maxv]; void addedge(int from,int to,int cap){///加边 G[from].pb(EDGE(to,cap,G[to].size())); G[to].pb(EDGE(from,0,G[from].size()-1)); } int s=maxv-1; int t=maxv-2; int level[maxv]; queue<int> Q; void bfs(int s){////bfs出分层图 memset(level,-1,sizeof level); level[s]=0; Q.push(s); while(!Q.empty()){ int v=Q.front();Q.pop(); for(int i=0;i<G[v].size();i++){ EDGE &e=G[v][i]; if(e.cap>0&&level[e.to]==-1){ level[e.to]=level[v]+1; Q.push(e.to); } } } } int iter[maxv]; int dfs(int v,int t,int f){///dfs寻找增广路径 if(v==t) return f; for(int &i=iter[v];i<G[v].size();i++){ EDGE &e=G[v][i]; if(e.cap>0&&level[e.to]>level[v]){ int d=dfs(e.to,t,min(f,e.cap)); if(d>0){ e.cap-=d; G[e.to][e.rev].cap+=d; return d; } } } return 0; } int dinic(int s,int t){///dinic算法求解最大流 int flow=0; for(;;){ bfs(s); if(level[t]==-1) return flow; memset(iter,0,sizeof iter); int f; while((f=dfs(s,t,IINF))>0) flow+=f; } return 0; } const int maxn=1200; int T; int m,S; int es[maxn][3]; int d[maxn]; vector<int> nG[maxn]; bool vis[maxn]; void init(){ memset(d,0,sizeof d); memset(vis,0,sizeof vis); G[s].clear();G[t].clear(); for(int i=1;i<=m;i++){ G[i].clear();nG[i].clear(); } } void dfs(int v){ vis[v]=1; for(int i=0;i<nG[v].size();i++){ int u=nG[v][i]; if(vis[u]) continue; dfs(u); } } int main(){ freopen("/home/slyfc/CppFiles/in","r",stdin); cin>>T; while(T--){ cin>>m>>S; init(); for(int i=0;i<S;i++){ scanf("%d%d%d",&es[i][0],&es[i][1],&es[i][2]); es[i][2]^=1; nG[es[i][0]].pb(es[i][1]); nG[es[i][1]].pb(es[i][0]); d[es[i][1]]++,d[es[i][0]]--; } dfs(1); bool f=1; for(int i=1;i<=m;i++) if(!vis[i]){ f=0; break; } for(int i=1;i<=m;i++){ if(d[i]%2!=0){ f=0;break; } } if(!f){ puts("impossible"); continue; } for(int i=0;i<S;i++){ int x=es[i][0],y=es[i][1],z=es[i][2]; if(z){ addedge(y,x,1); } } int sum=0; for(int i=1;i<=m;i++){ if(d[i]>0){ sum+=d[i]/2; addedge(s,i,d[i]/2); }else{ addedge(i,t,-(d[i]/2)); } } int res=dinic(s,t); if(res==sum){ puts("possible"); }else{ puts("impossible"); } } return 0; }
3 sgu194 Reactor Cooling(无源汇有上下界的网络流
思路见:http://www.cnblogs.com/kane0526/archive/2013/04/05/3001108.html
把有下界限制的边减去下界,然后增加超级源点汇点处理下界流量,如果下界流量满流则有解,否则无解。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * @author: Cwind */ ///#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define IOS std::ios::sync_with_stdio (false);std::cin.tie(0) #define pb push_back #define PB pop_back #define bk back() #define fs first #define se second #define sq(x) (x)*(x) #define eps (1e-6) #define IINF (1<<29) #define LINF (1ll<<59) #define INF (1000000000) #define FINF (1e3) #define clr(x) memset((x),0,sizeof (x)); typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> pii; typedef pair<ll,ll> P; const int maxv=100000; struct EDGE{ int to,cap,rev; int id; EDGE(int t,int c,int r,int id):to(t),cap(c),rev(r),id(id){} }; vector<EDGE> G[maxv]; void addedge(int from,int to,int cap,int id){///加边 G[from].pb(EDGE(to,cap,G[to].size(),id)); G[to].pb(EDGE(from,0,G[from].size()-1,-id)); } int s=maxv-1; int t=maxv-2; int level[maxv]; queue<int> Q; void bfs(int s){////bfs出分层图 memset(level,-1,sizeof level); level[s]=0; Q.push(s); while(!Q.empty()){ int v=Q.front();Q.pop(); for(int i=0;i<G[v].size();i++){ EDGE &e=G[v][i]; if(e.cap>0&&level[e.to]==-1){ level[e.to]=level[v]+1; Q.push(e.to); } } } } int iter[maxv]; int dfs(int v,int t,int f){///dfs寻找增广路径 if(v==t) return f; for(int &i=iter[v];i<G[v].size();i++){ EDGE &e=G[v][i]; if(e.cap>0&&level[e.to]>level[v]){ int d=dfs(e.to,t,min(f,e.cap)); if(d>0){ e.cap-=d; G[e.to][e.rev].cap+=d; return d; } } } return 0; } int dinic(int s,int t){///dinic算法求解最大流 int flow=0; for(;;){ bfs(s); if(level[t]==-1) return flow; memset(iter,0,sizeof iter); int f; while((f=dfs(s,t,IINF))>0) flow+=f; } return 0; } const int maxm=50000; int n,m; int es[maxm][4]; int a[4]; int d[maxm]; int ans[maxm]; int main(){ freopen("/home/slyfc/CppFiles/in","r",stdin); cin>>n>>m; for(int i=1;i<=m;i++){ for(int j=0;j<4;j++){scanf("%d",&a[j]);} for(int j=0;j<4;j++){es[i][j]=a[j];} addedge(a[0],a[1],a[3]-a[2],i); d[a[0]]-=a[2]; d[a[1]]+=a[2]; } int sum=0,sumn=0; for(int i=1;i<=n;i++){ if(d[i]>0){ sum+=d[i]; addedge(s,i,d[i],m+1); }else{ sumn+=-d[i]; addedge(i,t,-d[i],m+1); } } if(sum!=sumn){ puts("NO"); return 0; } int res=dinic(s,t); if(res==sum){ puts("YES"); for(int v=1;v<=n;v++){ for(int j=0;j<G[v].size();j++){ EDGE &e=G[v][j]; if(e.id<0){ ans[-e.id]=e.cap+es[-e.id][2]; } } } for(int i=1;i<=m;i++){ printf("%d\n",ans[i]); } }else{ puts("NO"); } return 0; }
4 zoj3229 Shoot the Bullet(有源汇有上下界的最大流
思路:在汇点到源点加一条容量无穷的边,然后方法同无源汇的情况。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * @author: Cwind */ ///#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define IOS std::ios::sync_with_stdio (false);std::cin.tie(0) #define pb push_back #define PB pop_back #define bk back() #define fs first #define se second #define sq(x) (x)*(x) #define eps (1e-6) #define IINF (1<<29) #define LINF (1ll<<59) #define INF (1000000000) #define FINF (1e3) #define clr(x) memset((x),0,sizeof (x)); typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> pii; typedef pair<ll,ll> P; const int maxv=100000; struct EDGE{ int to,cap,rev; EDGE(int t,int c,int r):to(t),cap(c),rev(r){} }; vector<EDGE> G[maxv]; void addedge(int from,int to,int cap){///加边 G[from].pb(EDGE(to,cap,G[to].size())); G[to].pb(EDGE(from,0,G[from].size()-1)); } int s=maxv-1; int t=maxv-2; int level[maxv]; queue<int> Q; void bfs(int s){////bfs出分层图 memset(level,-1,sizeof level); level[s]=0; Q.push(s); while(!Q.empty()){ int v=Q.front();Q.pop(); for(int i=0;i<G[v].size();i++){ EDGE &e=G[v][i]; if(e.cap>0&&level[e.to]==-1){ level[e.to]=level[v]+1; Q.push(e.to); } } } } int iter[maxv]; int dfs(int v,int t,int f){///dfs寻找增广路径 if(v==t) return f; for(int &i=iter[v];i<G[v].size();i++){ EDGE &e=G[v][i]; if(e.cap>0&&level[e.to]>level[v]){ int d=dfs(e.to,t,min(f,e.cap)); if(d>0){ e.cap-=d; G[e.to][e.rev].cap+=d; return d; } } } return 0; } int dinic(int s,int t){///dinic算法求解最大流 int flow=0; for(;;){ bfs(s); if(level[t]==-1) return flow; memset(iter,0,sizeof iter); int f; while((f=dfs(s,t,IINF))>0) flow+=f; } return 0; } int ss=s-3,tt=ss-1; const int maxm=1400; int n,m; int g[maxv]; int R[366][1005]; int d[maxv]; void init(){ for(int i=0;i<n+m+1;i++){ G[i].clear(); } G[s].clear();G[t].clear(); G[ss].clear();G[tt].clear(); clr(d);clr(R); } int main(){ freopen("/home/slyfc/CppFiles/in","r",stdin); while(cin>>n>>m){ init(); for(int i=1;i<=m;i++){ scanf("%d",&g[i]); addedge(i,t,INF); d[i]-=g[i],d[t]+=g[i]; } for(int i=1;i<=n;i++){ int C,D; scanf("%d%d",&C,&D); addedge(s,i+m,D); for(int j=1;j<=C;j++){ int tar,l,r; scanf("%d%d%d",&tar,&l,&r); tar++; addedge(i+m,tar,r-l); d[i+m]-=l,d[tar]+=l; R[i][tar]=r; } } int sum=0; addedge(ss,t,d[t]); sum+=d[t]; for(int i=1;i<=m+n;i++){ if(d[i]>0){ sum+=d[i]; addedge(ss,i,d[i]); }else{ addedge(i,tt,-d[i]); } } addedge(t,s,INF); int res=dinic(ss,tt); if(res!=sum){ puts("-1\n"); continue; } G[ss].clear();G[tt].clear(); int res2=dinic(s,t); printf("%d\n",res2); for(int i=1;i<=n;i++){ int v=i+m; for(int j=0;j<G[v].size();j++){ EDGE &e=G[v][j]; if(e.to<=m&&e.to>=1){ printf("%d\n",R[i][e.to]-e.cap); } } } puts(""); } return 0; }
5 sgu176(有上下界的最小流
思路:限制边流量建图,先跑ss到tt的最大流,加t到s的边,再跑一次ss到tt的最大流,注意加边必须跑一次再加。。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * @author: Cwind */ ///#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define IOS std::ios::sync_with_stdio (false);std::cin.tie(0) #define pb push_back #define PB pop_back #define bk back() #define fs first #define se second #define sq(x) (x)*(x) #define eps (1e-6) #define IINF (1<<29) #define LINF (1ll<<59) #define INF (1000000000) #define FINF (1e3) #define clr(x) memset((x),0,sizeof (x)); typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> pii; typedef pair<ll,ll> P; const int maxv=100000; struct EDGE{ int to,cap,rev; int id; EDGE(int t,int c,int r,int id):to(t),cap(c),rev(r),id(id){} }; vector<EDGE> G[maxv]; void addedge(int from,int to,int cap,int id){///加边 G[from].pb(EDGE(to,cap,G[to].size(),id)); G[to].pb(EDGE(from,0,G[from].size()-1,-id)); } int s=maxv-1; int t=maxv-2; int level[maxv]; queue<int> Q; void bfs(int s){////bfs出分层图 memset(level,-1,sizeof level); level[s]=0; Q.push(s); while(!Q.empty()){ int v=Q.front();Q.pop(); for(int i=0;i<G[v].size();i++){ EDGE &e=G[v][i]; if(e.cap>0&&level[e.to]==-1){ level[e.to]=level[v]+1; Q.push(e.to); } } } } int iter[maxv]; int dfs(int v,int t,int f){///dfs寻找增广路径 if(v==t) return f; for(int &i=iter[v];i<G[v].size();i++){ EDGE &e=G[v][i]; if(e.cap>0&&level[e.to]>level[v]){ int d=dfs(e.to,t,min(f,e.cap)); if(d>0){ e.cap-=d; G[e.to][e.rev].cap+=d; return d; } } } return 0; } int dinic(int s,int t){///dinic算法求解最大流 int flow=0; for(;;){ bfs(s); if(level[t]==-1) return flow; memset(iter,0,sizeof iter); int f; while((f=dfs(s,t,IINF))>0) flow+=f; } return 0; } const int maxm=10000; int ss=s-3,tt=ss-1; int n,m; int full[maxm]; int flow[maxm]; int main(){ freopen("/home/slyfc/CppFiles/in","r",stdin); cin>>n>>m; s=1,t=n; int sum=0; for(int i=1;i<=m;i++){ int a,b,c,d; scanf("%d%d%d%d",&a,&b,&c,&d); if(d){ full[i]=c; addedge(a,tt,c,i); addedge(ss,b,c,i); sum+=c; }else{ addedge(a,b,c,i); } } int res=dinic(ss,tt); addedge(t,s,INF,INF); int res2=dinic(ss,tt); if(res+res2!=sum){ puts("Impossible"); }else{ int ans=G[s].bk.cap; cout<<ans<<endl; for(int i=1;i<=n;i++){ for(int j=0;j<G[i].size();j++){ EDGE &e=G[i][j]; if(e.id<0&&-e.id<=m){ flow[-e.id]=e.cap; } } } for(int i=1;i<=m;i++){ printf("%d ",flow[i]); } } return 0; }
6 poj2699
题目:小于10个人进行锦标赛,共n*(n-1)/2场,如果某个人赢了所有胜场比他多的人,那么他非常叼,现在要求最多有多少人很叼。
思路:如果人数为k,那么必然可以构造出分数最大的人是叼人的分配方式,所以枚举人数即可。建图,对于必须要赢的场,胜者建边,其他场建两条边。源点向每个人建胜场条边。
然后跑最大流,同时可以得到分配方式。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * @author: Cwind */ ///#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define IOS std::ios::sync_with_stdio (false);std::cin.tie(0) #define pb push_back #define PB pop_back #define bk back() #define fs first #define se second #define sq(x) (x)*(x) #define eps (1e-7) #define IINF (1<<29) #define LINF (1ll<<59) #define INF (1000000000) #define FINF (1e3) #define clr(x) memset((x),0,sizeof (x)) #define cp(a,b) memcpy((a),(b),sizeof (a)) typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> pii; typedef pair<int,int> P; const int maxv=1000; struct EDGE{ int to,cap,rev; EDGE(int t,int c,int r):to(t),cap(c),rev(r){} }; vector<EDGE> G[maxv]; void addedge(int from,int to,int cap){///加边 G[from].pb(EDGE(to,cap,G[to].size())); G[to].pb(EDGE(from,0,G[from].size()-1)); } int s=maxv-1; int t=maxv-2; int level[maxv]; queue<int> Q; void bfs(int s){////bfs出分层图 memset(level,-1,sizeof level); level[s]=0; Q.push(s); while(!Q.empty()){ int v=Q.front();Q.pop(); for(int i=0;i<G[v].size();i++){ EDGE &e=G[v][i]; if(e.cap>0&&level[e.to]==-1){ level[e.to]=level[v]+1; Q.push(e.to); } } } } int iter[maxv]; int dfs(int v,int t,int f){///dfs寻找增广路径 if(v==t) return f; for(int &i=iter[v];i<G[v].size();i++){ EDGE &e=G[v][i]; if(e.cap>0&&level[e.to]>level[v]){ int d=dfs(e.to,t,min(f,e.cap)); if(d>0){ e.cap-=d; G[e.to][e.rev].cap+=d; return d; } } } return 0; } int dinic(int s,int t){///dinic算法求解最大流 int flow=0; for(;;){ bfs(s); if(level[t]==-1) return flow; memset(iter,0,sizeof iter); int f; while((f=dfs(s,t,IINF))>0) flow+=f; } return 0; } int T; char r[1000]; vector<int> a; int n; void get(){ a.clear(); int len=strlen(r); for(int i=0;i<len;i+=2){ a.pb(r[i]-'0'); } sort(a.begin(),a.end()); n=a.size(); } int ans; void build(int k){ for(int i=0;i<maxv;i++) G[i].clear(); for(int i=n-k;i<n;i++){ for(int j=i+1;j<n;j++){ addedge(i,(i+1)*n+j,1); if(a[i]==a[j]) addedge(j,(i+1)*n+j,1); } } for(int i=n;i<n*(n+2);i++){ addedge(i,t,1); } for(int i=0;i<n-k;i++){ for(int j=i+1;j<n;j++){ addedge(i,(i+1)*n+j,1); addedge(j,(i+1)*n+j,1); } } for(int i=0;i<n;i++){ addedge(s,i,a[i]); } } void solve(){ int tar=n*(n-1)/2; for(int i=1;i<=n;i++){ build(i); int d=dinic(s,t); if(d==tar) ans=i; else break; } printf("%d\n",ans); } int main(){ freopen("/home/slyfc/CppFiles/in","r",stdin); cin>>T; gets(r); while(T--){ gets(r); get(); solve(); } return 0; }
7 poj3084(最小割
题目:若干个房间,有些房间有入侵者,有些房间是要保护的,还有些只有一面能打开的锁,问最小关几道门保护所有需要保护的房间。
思路:最小割,不过对于关不住的点我是打算暴力dfs的,看到题解直接连一条INF的边,确实简洁高效。(代码暂时没有。。
8 PKU 3308(最小割
题目:要攻击l个格子,攻击每行或每列有个花费,求攻击到所有格子的最小花费。
思路:首先行列分开建图是常见方法,然后这个是求最小花费,考虑最小割求解。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * @author: Cwind */ ///#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define IOS std::ios::sync_with_stdio (false);std::cin.tie(0) #define pb push_back #define PB pop_back #define bk back() #define fs first #define se second #define sq(x) (x)*(x) #define eps (1e-10) #define IINF (1<<29) #define LINF (1ll<<59) #define INF (1000000300) #define FINF (1e9) #define clr(x) memset((x),0,sizeof (x)) #define cp(a,b) memcpy((a),(b),sizeof (b)) #define mset(x,v) memset((x),(v),sizeof (x)) template <class T> inline bool scan_d(T &ret) { char c; int sgn; if(c=getchar(),c==EOF) return 0; //EOF while(c!='-'&&(c<'0'||c>'9')) c=getchar(); sgn=(c=='-')?-1:1; ret=(c=='-')?0:(c-'0'); while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0'); ret*=sgn; return 1; } inline void out(long long x) { if(x>9) out(x/10); putchar(x%10+'0'); } typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> pii; typedef pair<ll,ll> P; const int maxv=2000; struct EDGE{ int to,next; double cap; }ES[maxv*2]; int head[maxv]; int eh; void addedge(int from,int to,double cap){///加边 ES[eh].to=to,ES[eh].cap=cap,ES[eh].next=head[from]; head[from]=eh++; ES[eh].to=from,ES[eh].cap=0,ES[eh].next=head[to]; head[to]=eh++; } int s=maxv-1; int t=maxv-2; int level[maxv]; queue<int> Q; void bfs(int s){////bfs出分层图 memset(level,-1,sizeof level); level[s]=0; Q.push(s); while(!Q.empty()){ int v=Q.front();Q.pop(); for(int i=head[v];i!=-1;i=ES[i].next){ EDGE &e=ES[i]; if(e.cap>0&&level[e.to]==-1){ level[e.to]=level[v]+1; Q.push(e.to); } } } } int iter[maxv]; double dfs(int v,int t,double f){///dfs寻找增广路径 if(v==t) return f; for(int &i=iter[v];i!=-1;i=ES[i].next){ EDGE &e=ES[i]; if(e.cap>0&&level[e.to]>level[v]){ double d=dfs(e.to,t,fmin(f,e.cap)); if(d>0){ e.cap-=d; ES[i^1].cap+=d; return d; } } } return 0; } double dinic(int s,int t){///dinic算法求解最大流 double flow=0; for(;;){ bfs(s); if(level[t]==-1) return flow; for(int i=0;i<maxv;i++) iter[i]=head[i]; double f; while((f=dfs(s,t,FINF))>0) flow+=f; } return 0; } void clear(){ memset(head,-1,sizeof head); eh=0; } const int maxn=1e5; int T; int n,m,l; double cm[maxn],cn[maxn]; int main(){ //freopen("/home/slyfc/CppFiles/in","r",stdin); cin>>T; while(T--){ clear(); scan_d(m);scan_d(n);scan_d(l); for(int i=0;i<m;i++){ scanf("%lf",&cm[i]); cm[i]=log(cm[i]); addedge(s,i,cm[i]); } for(int i=0;i<n;i++){ scanf("%lf",&cn[i]); cn[i]=log(cn[i]); addedge(i+m,t,cn[i]); } for(int i=0;i<l;i++){ int r,c; scan_d(r);scan_d(c); addedge(r-1,c-1+m,FINF); } double ans=dinic(s,t); printf("%.4f\n",exp(ans)); } return 0; }
9 poj 2125(二分图最小点权覆盖
题目:一个有向图,对于每个点,可以选择删除这个点所有的出边,或所有的出边。求删除图上所有边的最小花费。
思路:明显是一个二分图,一条边要么在入点删除,要么在出点删除,二者必有至少一个。建图之后转化为最小点权覆盖。对于每个网络上的路径,必有s,u,v,t的路径,由于u,v的边不可能在最小割上,那么割中必然有s,u,或v,t中的一条边。那么求得最小割后,从s不可达的点必然在割中,而从s可达的点,其割必然在v到t的边中,所以从s可达的vt边必然是割边(满流)否则有s到t的通路,注意满流的vt边不一定是割边。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * @author: Cwind */ ///#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define IOS std::ios::sync_with_stdio (false);std::cin.tie(0) #define pb push_back #define PB pop_back #define bk back() #define fs first #define se second #define sq(x) (x)*(x) #define eps (1e-10) #define IINF (1<<29) #define LINF (1ll<<59) #define INF (1000000300) #define FINF (1e9) #define clr(x) memset((x),0,sizeof (x)) #define cp(a,b) memcpy((a),(b),sizeof (b)) #define mset(x,v) memset((x),(v),sizeof (x)) template <class T> inline bool scan_d(T &ret) { char c; int sgn; if(c=getchar(),c==EOF) return 0; //EOF while(c!='-'&&(c<'0'||c>'9')) c=getchar(); sgn=(c=='-')?-1:1; ret=(c=='-')?0:(c-'0'); while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0'); ret*=sgn; return 1; } inline void out(long long x) { if(x>9) out(x/10); putchar(x%10+'0'); } typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> pii; typedef pair<int,int> P; const int maxv=1000; struct EDGE{ int to,cap,next; }ES[maxv*15]; int head[maxv]; int eh; void addedge(int from,int to,int cap){///加边 ES[eh].to=to,ES[eh].cap=cap,ES[eh].next=head[from]; head[from]=eh++; ES[eh].to=from,ES[eh].cap=0,ES[eh].next=head[to]; head[to]=eh++; } int s=maxv-1; int t=maxv-2; int level[maxv]; queue<int> Q; void bfs(int s){////bfs出分层图 memset(level,-1,sizeof level); level[s]=0; Q.push(s); while(!Q.empty()){ int v=Q.front();Q.pop(); for(int i=head[v];i!=-1;i=ES[i].next){ EDGE &e=ES[i]; if(e.cap>0&&level[e.to]==-1){ level[e.to]=level[v]+1; Q.push(e.to); } } } } int iter[maxv]; int dfs(int v,int t,int f){///dfs寻找增广路径 if(v==t) return f; for(int &i=iter[v];i!=-1;i=ES[i].next){ EDGE &e=ES[i]; if(e.cap>0&&level[e.to]>level[v]){ int d=dfs(e.to,t,min(f,e.cap)); if(d>0){ e.cap-=d; ES[i^1].cap+=d; return d; } } } return 0; } int dinic(int s,int t){///dinic算法求解最大流 int flow=0; for(;;){ bfs(s); if(level[t]==-1) return flow; for(int i=0;i<maxv;i++) iter[i]=head[i]; int f; while((f=dfs(s,t,IINF))>0){ flow+=f;; } } return 0; } void dinic_init(){ memset(head,-1,sizeof head); eh=0; } const int maxn=1000; int n,m; int w1[maxn],w2[maxn]; vector<P> oo; bool vis[maxv]; void getans(int v){ vis[v]=1; for(int i=head[v];i!=-1;i=ES[i].next){ EDGE &e=ES[i]; if(e.cap>0&&!vis[e.to]) getans(e.to); } } int main(){ freopen("/home/slyfc/CppFiles/in","r",stdin); while(cin>>n>>m){ dinic_init(); for(int i=1;i<=n;i++){ scan_d(w1[i]); addedge(s,i,w1[i]); } for(int i=1;i<=n;i++){ scan_d(w2[i]); addedge(i+n,t,w2[i]); } for(int i=0;i<m;i++){ int x,y; scan_d(x);scan_d(y); addedge(y,x+n,INF); } int ans=dinic(s,t); out(ans); puts(""); getans(s); for(int i=1;i<=n;i++) if(!vis[i]) oo.pb(P(i,1)); for(int i=n+1;i<=n+n;i++) if(vis[i]) oo.pb(P(i-n,0)); out(oo.size()); puts(""); for(int i=0;i<oo.size();i++) printf("%d %c\n",oo[i].fs,oo[i].se==0?'-':'+'); } return 0; }
10 POJ 1486(二分匹配必须边
题目:有若干矩形和若干标号,只要是在矩形内的就有可能是该矩形的标号,问有多少矩形的标号是确定的。
思路:先跑匈牙利做一次二分匹配,然后枚举每个匹配中的边,删除该边,如果匹配数目不变则不是必须边,否则为必须边。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * @author: Cwind */ ///#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define IOS std::ios::sync_with_stdio (false);std::cin.tie(0) #define pb push_back #define PB pop_back #define bk back() #define fs first #define se second #define sq(x) (x)*(x) #define eps (1e-10) #define IINF (1<<29) #define LINF (1ll<<59) #define INF (1000000300) #define FINF (1e9) #define clr(x) memset((x),0,sizeof (x)) #define cp(a,b) memcpy((a),(b),sizeof (b)) #define mset(x,v) memset((x),(v),sizeof (x)) typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> pii; typedef pair<int,int> P; const int maxn=60; int n; struct juxing{ int x[2],y[2]; }A[maxn]; P B[maxn]; bool ison(P a,juxing x){ return (a.fs<x.x[1]&&a.fs>x.x[0])&&(a.se<x.y[1]&&a.se>x.y[0]); } bool mat[maxn*2][maxn*2]; bool vis[maxn]; int match[maxn]; int V; bool dfs(int v){ vis[v]=1; for(int i=1;i<=V;i++){ int w=match[i]; if(!mat[v][i]) continue; if(w==-1||!vis[w]&&dfs(w)){ match[v]=i; match[i]=v; return 1; } } return 0; } void bi_match(){ memset(match,-1,sizeof match); for(int i=1;i<=V;i++){ if(match[i]==-1){ memset(vis,0,sizeof vis); dfs(i); } } } void init(){ memset(mat,0,sizeof mat); } vector<P> ans; void solve(){ ans.clear(); for(int i=1;i<=n;i++){ if(match[i]!=-1){ int w=match[i]; match[i]=match[w]=-1; mat[i][w]=mat[w][i]=0; memset(vis,0,sizeof vis); if(!dfs(i)){ ans.pb(P(w-n,i)); match[i]=w; match[w]=i; } mat[w][i]=mat[i][w]=1; } } } int cas; int main(){ freopen("/home/slyfc/CppFiles/in","r",stdin); while(scanf("%d",&n),n!=0){ V=n*2; init(); for(int i=1;i<=n;i++){ scanf("%d%d%d%d",&A[i].x[0],&A[i].x[1],&A[i].y[0],&A[i].y[1]); } for(int i=1;i<=n;i++){ scanf("%d%d",&B[i].fs,&B[i].se); } for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(ison(B[i],A[j])){ mat[i][j+n]=mat[j+n][i]=1; } } } printf("Heap %d\n",++cas); bi_match(); solve(); if(!ans.size()){ puts("none"); }else{ sort(ans.begin(),ans.end()); for(int i=0;i<ans.size();i++){ printf("(%c,%d) ",(char)(ans[i].fs+'A'-1),ans[i].se); } puts(""); } puts(""); } return 0; }
11 poj 3692(两个完全图组成的二分图的最大完全匹配
题目:每个女孩都互相认识,每个男孩也互相认识,还有若干对男女认识。然后求最大的一组人,组内都互相认识。
思路:这个图的补是个二分图,然后求这个二分图的最大独立集即可(此时独立集是互相没有边的最大组,也就是互相都认识!)。思路挺巧妙的。。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * @author: Cwind */ ///#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define IOS std::ios::sync_with_stdio (false);std::cin.tie(0) #define pb push_back #define PB pop_back #define bk back() #define fs first #define se second #define sq(x) (x)*(x) #define eps (1e-10) #define IINF (1<<29) #define LINF (1ll<<59) #define INF (1000000300) #define FINF (1e9) #define clr(x) memset((x),0,sizeof (x)) #define cp(a,b) memcpy((a),(b),sizeof (b)) #define mset(x,v) memset((x),(v),sizeof (x)) typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> pii; typedef pair<int,int> P; const int maxn=600; int match[maxn]; bool used[maxn]; bool mat[maxn][maxn]; int g,b,m; int V; bool dfs(int v){ used[v]=1; for(int i=1;i<=V;i++){ int u=i; if(mat[v][u]||(i<=g&&v<=g||i>g&&v>g)) continue; int w=match[u]; if(w==-1||!used[w]&&dfs(w)){ match[v]=u; match[u]=v; return 1; } } return 0; } int bi_match(){ memset(match,-1,sizeof match); int cnt=0; for(int i=1;i<=V;i++){ if(match[i]==-1){ memset(used,0,sizeof used); if(dfs(i)) cnt++; } } return cnt; } void init(){ memset(mat,0,sizeof mat); } int cas; int main(){ freopen("/home/slyfc/CppFiles/in","r",stdin); while(scanf("%d%d%d",&g,&b,&m)&&!(!g&&!b&&!m)){ init(); V=g+b; for(int i=0;i<m;i++){ int x,y; scanf("%d%d",&x,&y); mat[x][y+g]=mat[y+g][x]=1; } printf("Case %d: %d\n",++cas,g+b-bi_match()); } return 0; }
12 poj 2226(二分匹配,最小点覆盖
题目:在一个r×c的网格上有若干个点需要覆盖,每次可以覆盖连续的多个在一条直线上的点,可以重复覆盖。问最小需要覆盖多少次。
思路:每个点只有两种可能的覆盖方式,横着或者竖着,我们把两种覆盖方式建成点,如果两块板子可以覆盖同一个点,就在这两个板子之间连一条边,那么就是二分图的最小点覆盖问题,等于二分图的最大匹配。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * @author: Cwind */ ///#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define pb push_back #define PB pop_back #define bk back() #define fs first #define se second #define sq(x) (x)*(x) #define eps (1e-10) #define INF (1000000300) #define clr(x) memset((x),0,sizeof (x)) #define cp(a,b) memcpy((a),(b),sizeof (b)) typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> P; const int maxn=6000; int match[maxn]; bool used[maxn]; vector<int> G[maxn]; bool dfs(int v){ used[v]=1; for(int i=0;i<G[v].size();i++){ int u=G[v][i]; int w=match[u]; if(w==-1||!used[w]&&dfs(w)){ match[v]=u; match[u]=v; return 1; } } return 0; } int cas; char B[100][100]; char r[100]; int R,C; int sz; int id[maxn]; int max_match(){ memset(match,-1,sizeof match); int cnt=0; for(int i=R*C;i<=sz;i++){ if(match[i]==-1){ memset(used,0,sizeof used); if(dfs(i)) cnt++; } } return cnt; } void build(){ for(int i=0;i<R;i++){ for(int j=0;j<C;j++){ if(B[i][j]=='*'){ sz++; while(B[i][j]=='*'){ id[i*C+j]=sz; j++; } } } } for(int j=0;j<C;j++){ for(int i=0;i<R;i++){ if(B[i][j]=='*'){ sz++; while(B[i][j]=='*'){ int xx=i*C+j; G[sz].pb(id[xx]); G[id[xx]].pb(sz); i++; } } } } } int main(){ freopen("/home/slyfc/CppFiles/in","r",stdin); while(scanf("%d%d",&R,&C)!=EOF){ sz=R*C-1; for(int i=0;i<R;i++){ scanf("%s",r); for(int j=0;j<C;j++){ B[i][j]=r[j]; } } build(); int ma=max_match(); printf("%d\n",ma); } return 0; }
13 poj 2195(二分图最大匹配
题目:有n个人和n个房子,每个人分配一个房子,要求使得总移动距离之和最小的分配。
思路:二分图最小权匹配,模板题。也可以用费用流做。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * @author: Cwind */ ///#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define pb push_back #define PB pop_back #define bk back() #define fs first #define se second #define sq(x) (x)*(x) #define eps (1e-10) #define INF (1000000300) #define clr(x) memset((x),0,sizeof (x)) #define cp(a,b) memcpy((a),(b),sizeof (b)) typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> P; const int N = 310; int nx,ny;//两边的点数 int g[N][N];//二分图描述 int linker[N],lx[N],ly[N];//y中各点匹配状态,x,y中的点标号 int slack[N]; bool visx[N],visy[N]; bool DFS(int x){ visx[x] = true; for(int y = 0; y < ny; y++){ if(visy[y])continue; int tmp = lx[x] + ly[y] - g[x][y]; if(tmp == 0){ visy[y] = true; if(linker[y] == -1 || DFS(linker[y])){ linker[y] = x; return true; } } else if(slack[y] > tmp) slack[y] = tmp; } return false; } int KM(){ memset(linker,-1,sizeof(linker)); memset(ly,0,sizeof(ly)); for(int i = 0;i < nx;i++){ lx[i] = -INF; for(int j = 0;j < ny;j++){ if(g[i][j] > lx[i]) lx[i] = g[i][j]; } } for(int x = 0;x < nx;x++){ for(int i = 0;i < ny;i++) slack[i] = INF; while(true){ memset(visx,false,sizeof(visx)); memset(visy,false,sizeof(visy)); if(DFS(x))break; int d = INF; for(int i = 0;i < ny;i++){ if(!visy[i] && d > slack[i]) d = slack[i]; } for(int i = 0;i < nx;i++){ if(visx[i]) lx[i] -= d; } for(int i = 0;i < ny;i++){ if(visy[i])ly[i] += d; else slack[i] -= d; } } } int res = 0; for(int i = 0;i < ny;i++){ if(linker[i] != -1) res += g[linker[i]][i]; } return res; } int n,m; vector<P> house,man; char B[300][300]; char r[400]; void init(){ house.clear();man.clear(); } void build(){ nx=house.size();ny=man.size(); for(int i=0;i<nx;i++){ for(int j=0;j<ny;j++){ g[i][j]=-(abs(house[i].fs-man[j].fs)+abs(house[i].se-man[j].se)); } } } int main(){ freopen("/home/slyfc/CppFiles/in","r",stdin); while(scanf("%d%d",&n,&m)&&!(!n&&!m)){ init(); for(int i=0;i<n;i++){ scanf("%s",r); for(int j=0;j<m;j++){ if(r[j]=='H') house.pb(P(i,j)); if(r[j]=='m') man.pb(P(i,j)); } } build(); int ans=KM(); printf("%d\n",-ans); } return 0; }
14 Aizu 2251(最小路径覆盖
题目:有L个请求在t时间把东西送到p地,问最少要几个快递员。
思路:每个请求拆成两个点,如果从一个点能及时赶到另一个点就建一条边,然后跑二分匹配。。。。。(写好了结果断网,,,然后就这样了吧。。。。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * @author: Cwind */ ///#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define pb push_back #define PB pop_back #define bk back() #define fs first #define se second #define sq(x) (x)*(x) #define eps (1e-10) #define INF (1000000300) #define clr(x) memset((x),0,sizeof (x)) #define cp(a,b) memcpy((a),(b),sizeof (b)) typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> P; const int maxn=6000; int match[maxn]; bool used[maxn]; vector<int> G[maxn]; bool dfs(int v){ used[v]=1; for(int i=0;i<G[v].size();i++){ int u=G[v][i]; int w=match[u]; if(w==-1||!used[w]&&dfs(w)){ match[v]=u; match[u]=v; return 1; } } return 0; } int N,M,L; int max_match(){ memset(match,-1,sizeof match); int cnt=0; for(int i=1;i<=L*2;i++){ if(match[i]==-1){ memset(used,0,sizeof used); if(dfs(i)) cnt++; } } return cnt; } int mat[150][150]; void floyd(){ for(int k=0;k<N;k++) for(int i=0;i<N;i++) for(int j=0;j<N;j++) mat[i][j]=min(mat[i][j],mat[i][k]+mat[k][j]); } P di[2000]; void build(){ for(int i=1;i<=L;i++){ for(int j=1;j<=L;j++){ if(j==i) continue; if(mat[di[i].fs][di[j].fs]<=di[j].se-di[i].se){ G[i].pb(j+L); G[j+L].pb(i); } } } } void init(){ for(int i=0;i<maxn;i++) G[i].clear(); memset(mat,0x3f,sizeof mat); for(int i=0;i<150;i++) mat[i][i]=0; } int main(){ ////freopen("/home/slyfc/CppFiles/in","r",stdin); while(scanf("%d%d%d",&N,&M,&L)&&!(!N&&!M&&!L)){ init(); for(int i=0;i<M;i++){ int x,y,z; scanf("%d%d%d",&x,&y,&z); mat[x][y]=z; mat[y][x]=z; } floyd(); for(int i=1;i<=L;i++) scanf("%d%d",&di[i].fs,&di[i].se); build(); int ans=max_match(); printf("%d\n",L-ans); } return 0; }
15 poj 3068(费用流
题目:求s到t的两条最短不相交路径。
思路:费用流裸题,拆点跑即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * @author: Cwind */ ///#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define pb push_back #define PB pop_back #define bk back() #define fs first #define se second #define sq(x) (x)*(x) #define eps (1e-10) #define INF (1000000300) #define clr(x) memset((x),0,sizeof (x)) #define cp(a,b) memcpy((a),(b),sizeof (b)) typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> P; const int MAXV=1e5+300; int V; const int s=MAXV-1,t=MAXV-2; struct EDGE{ int to,cap,cost,next; }ES[MAXV*10]; int eh; int h[MAXV]; int dis[MAXV]; int prevv[MAXV],preve[MAXV]; int head[MAXV]; void addedge(int from,int to,int cap,int cost){ ES[eh].to=to;ES[eh].cap=cap;ES[eh].cost=cost; ES[eh].next=head[from];head[from]=eh++; ES[eh].to=from;ES[eh].cap=0;ES[eh].cost=-cost; ES[eh].next=head[to];head[to]=eh++; } ll min_cost_flow(int s,int t,int f){ V=MAXV;//default V size maxed ll res=0; memset(h,0,sizeof h); while(f>0){ priority_queue<P,vector<P> ,greater<P> >Q;////Dijkstra计算势h fill(dis,dis+V,INF); dis[s]=0; Q.push(P(0,s)); while(!Q.empty()){ P p=Q.top();Q.pop(); int v=p.se; if(dis[v]<p.fs) continue; for(int i=head[v];i!=-1;i=ES[i].next){ EDGE &e=ES[i]; if(e.cap>0&&dis[e.to]>dis[v]+e.cost+h[v]-h[e.to]){ dis[e.to]=dis[v]+e.cost +h[v]-h[e.to]; prevv[e.to]=v; preve[e.to]=i; Q.push(P(dis[e.to],e.to)); } } } if(dis[t]==INF){ return -1; } for(int v=0;v<V;v++){ h[v]+=dis[v]; } int d=f; for(int v=t;v!=s;v=prevv[v]){ d=min(d,ES[preve[v]].cap); } f-=d; res+=d*h[t]; for(int v=t;v!=s;v=prevv[v]){ EDGE &e=ES[preve[v]]; e.cap-=d; ES[preve[v]^1].cap+=d; } } return res; } void clear_G(){ memset(head,-1,sizeof head); } ////end int N,M; int cas; int main(){ freopen("/home/slyfc/CppFiles/in","r",stdin); while(cin>>N>>M,!(!N&&!M)){ clear_G(); for(int i=0;i<M;i++){ int x,y,z; scanf("%d%d%d",&x,&y,&z); addedge(x+N,y,1,z); } for(int i=0;i<N;i++){ addedge(i,i+N,1,0); } int ans=min_cost_flow(N,N-1,2); if(ans==-1){ printf("Instance #%d: Not possible\n",++cas); }else{ printf("Instance #%d: %d\n",++cas,ans); } } return 0; }
16 poj 3422(费用流
题目:给出一个矩阵,每个格子上有些钱,每次只能向下或向右走,问走k次最多能拿到多少钱。
思路:拆点,然后建一条费用为1的边,再建一条费用为INF的边,即可保证每个点只走一次。然后跑流量为k的费用流。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * @author: Cwind */ ///#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define pb push_back #define PB pop_back #define bk back() #define fs first #define se second #define sq(x) (x)*(x) #define eps (1e-10) #define INF (1000000300) #define clr(x) memset((x),0,sizeof (x)) #define cp(a,b) memcpy((a),(b),sizeof (b)) typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> P; const int MAXV=1e5+300; int V; const int s=MAXV-1,t=MAXV-2; struct EDGE{ int to,cap,cost,next; }ES[MAXV*10]; int eh; int h[MAXV]; int dis[MAXV]; int prevv[MAXV],preve[MAXV]; int head[MAXV]; void addedge(int from,int to,int cap,int cost){ ES[eh].to=to;ES[eh].cap=cap;ES[eh].cost=cost; ES[eh].next=head[from];head[from]=eh++; ES[eh].to=from;ES[eh].cap=0;ES[eh].cost=-cost; ES[eh].next=head[to];head[to]=eh++; } ll min_cost_flow(int s,int t,int f){ V=MAXV;//default V size maxed ll res=0; memset(h,0,sizeof h); while(f>0){ priority_queue<P,vector<P> ,greater<P> >Q;////Dijkstra计算势h fill(dis,dis+V,INF); dis[s]=0; Q.push(P(0,s)); while(!Q.empty()){ P p=Q.top();Q.pop(); int v=p.se; if(dis[v]<p.fs) continue; for(int i=head[v];i!=-1;i=ES[i].next){ EDGE &e=ES[i]; if(e.cap>0&&dis[e.to]>dis[v]+e.cost+h[v]-h[e.to]){ dis[e.to]=dis[v]+e.cost +h[v]-h[e.to]; prevv[e.to]=v; preve[e.to]=i; Q.push(P(dis[e.to],e.to)); } } } if(dis[t]==INF){ return -1; } for(int v=0;v<V;v++){ h[v]+=dis[v]; } int d=f; for(int v=t;v!=s;v=prevv[v]){ d=min(d,ES[preve[v]].cap); } f-=d; res+=d*h[t]; for(int v=t;v!=s;v=prevv[v]){ EDGE &e=ES[preve[v]]; e.cap-=d; ES[preve[v]^1].cap+=d; } } return res; } void clear_G(){ memset(head,-1,sizeof head); } ////end int N,K; int cas; int mat[1000][1000]; void build(){ for(int i=0;i<N;i++){ for(int j=0;j<N;j++){ addedge(i*N+j,i*N+j+N*N,1,-mat[i][j]); addedge(i*N+j,i*N+j+N*N,INF,0); if(i<N-1) addedge(i*N+j+N*N,(i+1)*N+j,INF,0); if(j<N-1) addedge(i*N+j+N*N,i*N+j+1,INF,0); } } } int main(){ freopen("/home/slyfc/CppFiles/in","r",stdin); while(cin>>N>>K){ clear_G(); for(int i=0;i<N;i++){ for(int j=0;j<N;j++){ scanf("%d",&mat[i][j]); } } build(); int ans=min_cost_flow(0,(N-1)*N+N-1+N*N,K); printf("%d\n",-ans); } return 0; }
17 poj3680(费用流
题目:给出若干带权区间,要求在任意一个点不被超过k个区间覆盖的情况下总的权值和最大。
思路:可以用费用流巧妙地解决这个区间选择的问题。首先把所有端点排序,然后依次从前一个点到后一个点连边。对于一个区间,从起始端点向区间终点连一条容量为1费用为权值负数的边。显然,对于任意区间,如果有流量流过,对应选择了这个区间。对于一个点,如果选择了k个覆盖这个点的区间,那么就不会有流量到达这个点,符合题目要求。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * @author: Cwind */ ///#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define pb push_back #define PB pop_back #define bk back() #define fs first #define se second #define sq(x) (x)*(x) #define eps (1e-10) #define INF (1000000300) #define clr(x) memset((x),0,sizeof (x)) #define cp(a,b) memcpy((a),(b),sizeof (b)) typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> P; typedef pair<ll,int> D; const int MAXV=500; int V; const int s=MAXV-1,t=MAXV-2; struct EDGE{ int to,cap,cost,next; }ES[MAXV*10]; int eh; int h[MAXV]; int dis[MAXV]; int prevv[MAXV],preve[MAXV]; int head[MAXV]; void addedge(int from,int to,int cap,int cost){ ES[eh].to=to;ES[eh].cap=cap;ES[eh].cost=cost; ES[eh].next=head[from];head[from]=eh++; ES[eh].to=from;ES[eh].cap=0;ES[eh].cost=-cost; ES[eh].next=head[to];head[to]=eh++; } bool inq[MAXV]; ll min_cost_flow(int s,int t,int f){ //V=MAXV;//default V size maxed ll res=0; memset(h,0,sizeof h); queue<P> Q;////spfa计算势h fill(dis,dis+V,INF); dis[s]=0; Q.push(P(0,s)); inq[s]=1; while(!Q.empty()){ P p=Q.front();Q.pop(); int v=p.se; inq[v]=0; for(int i=head[v];i!=-1;i=ES[i].next){ EDGE &e=ES[i]; if(e.cap>0&&dis[e.to]>dis[v]+e.cost+h[v]-h[e.to]){ dis[e.to]=dis[v]+e.cost +h[v]-h[e.to]; prevv[e.to]=v; preve[e.to]=i; if(!inq[e.to]) Q.push(P(dis[e.to],e.to)),inq[e.to]=1; } } } for(int v=0;v<V;v++) h[v]+=dis[v]; while(f>0){ priority_queue<P,vector<P> ,greater<P> >Q;////Dijkstra计算势h fill(dis,dis+V,INF); dis[s]=0; Q.push(P(0,s)); while(!Q.empty()){ P p=Q.top();Q.pop(); int v=p.se; if(dis[v]<p.fs) continue; for(int i=head[v];i!=-1;i=ES[i].next){ EDGE &e=ES[i]; if(e.cap>0&&dis[e.to]>dis[v]+e.cost+h[v]-h[e.to]){ dis[e.to]=dis[v]+e.cost +h[v]-h[e.to]; prevv[e.to]=v; preve[e.to]=i; Q.push(P(dis[e.to],e.to)); } } } if(dis[t]==INF) return -1; for(int v=0;v<V;v++) h[v]+=dis[v]; int d=f; for(int v=t;v!=s;v=prevv[v]) d=min(d,ES[preve[v]].cap); f-=d; res+=d*h[t]; for(int v=t;v!=s;v=prevv[v]){ EDGE &e=ES[preve[v]]; e.cap-=d; ES[preve[v]^1].cap+=d; } } return res; } void clear_G(){ eh=0; memset(head,-1,sizeof head); } const int maxn=300; int N,K; int a[maxn],b[maxn],w[maxn]; int dc[maxn*2]; int hh; int main(){ ///freopen("/home/slyfc/CppFiles/in","r",stdin); //freopen("/home/slyfc/CppFiles/out","w",stdout); int T; cin>>T; while(T--){ cin>>N>>K; clear_G(); for(int i=0;i<N;i++){ scanf("%d%d%d",&a[i],&b[i],&w[i]); dc[i]=a[i],dc[i+N]=b[i]; } sort(dc,dc+N*2); hh=unique(dc,dc+N*2)-dc; V=hh+1; for(int i=1;i<hh;i++) addedge(i,i+1,INF,0); for(int i=0;i<N;i++){ a[i]=lower_bound(dc,dc+hh,a[i])-dc+1; b[i]=lower_bound(dc,dc+hh,b[i])-dc+1; } for(int i=0;i<N;i++) addedge(a[i],b[i],1,-w[i]); int ans=min_cost_flow(1,hh,K); printf("%d\n",-ans); } return 0; }
用dijkstra优化的费用流是比裸跑spfa要快不少。
18 SPOJ BOXES(费用流
题目:给出一排盒子,里面有若干球,要求用最小的步数让每个盒子里至多有一个球.
思路:费用流裸题.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * @author: Cwind */ #pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define IOS std::ios::sync_with_stdio (false);std::cin.tie(0) #define pb push_back #define PB pop_back #define bk back() #define fs first #define se second #define sq(x) (x)*(x) #define eps (1e-10) #define INF (1000000300) #define clr(x) memset((x),0,sizeof (x)) #define cp(a,b) memcpy((a),(b),sizeof (b)) typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> P; ////链表版 const int MAXV=1200; int V; const int s=MAXV-1,t=MAXV-2; struct EDGE{ int to,cap,cost,next; }ES[MAXV*10]; int eh; int h[MAXV]; int dis[MAXV]; int prevv[MAXV],preve[MAXV]; int head[MAXV]; void addedge(int from,int to,int cap,int cost){ ES[eh].to=to;ES[eh].cap=cap;ES[eh].cost=cost; ES[eh].next=head[from];head[from]=eh++; ES[eh].to=from;ES[eh].cap=0;ES[eh].cost=-cost; ES[eh].next=head[to];head[to]=eh++; } bool inq[MAXV]; ll min_cost_flow(int s,int t,int f){ V=MAXV;//default V size maxed ll res=0; memset(h,0,sizeof h); /*queue<P> Q;////spfa计算势h fill(dis,dis+V,INF); dis[s]=0; Q.push(P(0,s)); inq[s]=1; while(!Q.empty()){ P p=Q.front();Q.pop(); int v=p.se; inq[v]=0; for(int i=head[v];i!=-1;i=ES[i].next){ EDGE &e=ES[i]; if(e.cap>0&&dis[e.to]>dis[v]+e.cost+h[v]-h[e.to]){ dis[e.to]=dis[v]+e.cost +h[v]-h[e.to]; prevv[e.to]=v; preve[e.to]=i; if(!inq[e.to]) Q.push(P(dis[e.to],e.to)),inq[e.to]=1; } } } for(int v=0;v<V;v++) h[v]+=dis[v];*/ while(f>0){ priority_queue<P,vector<P> ,greater<P> >Q;////Dijkstra计算势h,也可裸跑spfa fill(dis,dis+V,INF); dis[s]=0; Q.push(P(0,s)); while(!Q.empty()){ P p=Q.top();Q.pop(); int v=p.se; if(dis[v]<p.fs) continue; for(int i=head[v];i!=-1;i=ES[i].next){ EDGE &e=ES[i]; if(e.cap>0&&dis[e.to]>dis[v]+e.cost+h[v]-h[e.to]){ dis[e.to]=dis[v]+e.cost +h[v]-h[e.to]; prevv[e.to]=v; preve[e.to]=i; Q.push(P(dis[e.to],e.to)); } } } if(dis[t]==INF) return -1; for(int v=0;v<V;v++) h[v]+=dis[v]; int d=f; for(int v=t;v!=s;v=prevv[v]) d=min(d,ES[preve[v]].cap); f-=d; res+=d*h[t]; for(int v=t;v!=s;v=prevv[v]){ EDGE &e=ES[preve[v]]; e.cap-=d; ES[preve[v]^1].cap+=d; } } return res; } void clear_G(){ eh=0; memset(head,-1,sizeof head); } const int maxn=1005; int n,T; int a[maxn]; int main(){ freopen("/home/slyfc/CppFiles/in","r",stdin); //freopen("/home/slyfc/CppFiles/out","w",stdout); cin>>T; while(T--){ clear_G(); scanf("%d",&n); int sum=0; for(int i=1;i<=n;i++){ scanf("%d",&a[i]); sum+=a[i]; } for(int i=1;i<n;i++){ addedge(i,i+1,INF,1); } for(int i=2;i<=n;i++){ addedge(i,i-1,INF,1); } addedge(n,1,INF,1); addedge(1,n,INF,1); for(int i=1;i<=n;i++){ addedge(s,i,a[i],0); addedge(i,t,1,0); } int ans=min_cost_flow(s,t,sum); printf("%d\n",ans); } return 0; }
19 SGU 185(费用流
题目:要求两条从s到t的最短路径,并输出.
思路:sgu卡常数简直sxbk......而且这题是卡空间........首先上去就是费用流然后直接mle,后来又改成先最短路然后再dinic还是mle,最后把dinic的图用链表存才过......还要注意的一点是输出,要有流量并且保证方向正确.....其实这题如果不是先做最短路的话输出似乎还比较麻烦.....
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * @author: Cwind */ ///#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define pb push_back #define PB pop_back #define bk back() #define fs first #define se second #define sq(x) (x)*(x) #define eps (1e-10) #define INF (1000000300) #define clr(x) memset((x),0,sizeof (x)) #define cp(a,b) memcpy((a),(b),sizeof (b)) typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> P; const int maxv=405; int mat[maxv][maxv]; int dis[maxv]; int N,M; int dij(){ fill(dis,dis+maxv,INF); priority_queue<P,vector<P>,greater<P> > Q; Q.push(P(0,1)); dis[1]=0; while(!Q.empty()){ int v=Q.top().se,d=Q.top().fs;Q.pop(); if(dis[v]<d) continue; for(int i=1;i<=N;i++){ if(dis[i]>dis[v]+mat[i][v]){ dis[i]=dis[v]+mat[i][v]; Q.push(P(dis[i],i)); } } } return dis[N]; } const int MAXV=405; const int MAXE=MAXV*MAXV; struct EDGE{ int to,cap,next; }ES[MAXE]; int head[MAXV]; int eh; void addedge(int from,int to,int cap){///加边 ES[eh].to=to,ES[eh].cap=cap,ES[eh].next=head[from]; head[from]=eh++; ES[eh].to=from,ES[eh].cap=0,ES[eh].next=head[to]; head[to]=eh++; } int s=MAXV-1; int t=MAXV-2; int level[MAXV]; queue<int> Q; void bfs(int s){////bfs出分层图 memset(level,-1,sizeof level); level[s]=0; Q.push(s); while(!Q.empty()){ int v=Q.front();Q.pop(); for(int i=head[v];i!=-1;i=ES[i].next){ EDGE &e=ES[i]; if(e.cap>0&&level[e.to]==-1){ level[e.to]=level[v]+1; Q.push(e.to); } } } } int iter[MAXV]; int dfs(int v,int t,int f){///dfs寻找增广路径 if(v==t) return f; for(int &i=iter[v];i!=-1;i=ES[i].next){ EDGE &e=ES[i]; if(e.cap>0&&level[e.to]>level[v]){ int d=dfs(e.to,t,min(f,e.cap)); if(d>0){ e.cap-=d; ES[i^1].cap+=d; return d; } } } return 0; } int dinic(int s,int t){///dinic算法求解最大流 int flow=0; for(;;){ bfs(s); if(level[t]==-1) return flow; for(int i=0;i<MAXV;i++) iter[i]=head[i]; int f; while((f=dfs(s,t,INF))>0) flow+=f; } return 0; } void dinic_init(){ memset(head,-1,sizeof head); eh=0; } void build(){ for(int i=1;i<=N;i++){ for(int j=1;j<=N;j++){ if(dis[j]==dis[i]+mat[i][j]) addedge(i,j,1); } } } vector<int> rout; void out(int v){ rout.pb(v); if(v==1) return; for(int i=head[v];i!=-1;i=ES[i].next){ EDGE &e=ES[i]; if(!e.cap||(i&1)==0) continue; e.cap=0; out(e.to); break; } } int main(){ freopen("/home/slyfc/CppFiles/in","r",stdin); dinic_init(); memset(mat,0x3f,sizeof mat); cin>>N>>M; for(int i=0;i<M;i++){ int x,y,z; scanf("%d%d%d",&x,&y,&z); mat[x][y]=z; mat[y][x]=z; } int mindis=dij(); build(); int maxflow=dinic(1,N); if(mindis>1e9||maxflow<2){ puts("No solution"); }else{ out(N); for(int i=rout.size()-1;i>=0;i--) printf("%d ",rout[i]); puts(""); rout.clear(); out(N); for(int i=rout.size()-1;i>=0;i--) printf("%d ",rout[i]); } return 0; }