一、有上下界的网络流:
sgu 194 (无源汇的上下界网络流):
题意:给出一个网络,每条边有上下界,问是否存在可行流。如果存在,输出经过每条边的流量。
建图:每条边必须有li的流量,那么新建超级源点和超级汇点,然后统计每个点的“入度”和“出度”,然后源点和每个点建一条流量为“入度”的边,每个点和汇点建一条流量为“出度”的边。为了流量平衡,每条边取原有上界-原有下界为流量,然后加入网络。(还有种做法是每个点统计出度和入度,判断是正是负来决定它和源点还是汇点建边。这种做法运行效率更高一些。)
题目给的图因为没有源点和汇点,所以我觉得统计最大流是没有意义的。对于我们自己建的图,判断一下每条边的下界的和是否等于最大流,以此来判定是否有可行流就行了。
#include <iostream> #include <cstdio> #include <cstdlib> #include <algorithm> #include <cstring> #include <ctime> #define FORD(i,r,l) for(int i=(r);i>=(l);i--) #define rep(i,n) for(int i=0;i<(n);i++) #define req(i,l,n) for(int i=(l);i<(n);i++) using namespace std; typedef long long LL; const int maxn=205+1,maxm=120000; const LL jx=0x7fffffffffffffff; int pos,ta[maxn],lin[maxm],sd[maxm],sl[maxm]; void biu(int s,int t,int c) { ++pos; lin[pos]=ta[s]; ta[s]=pos; sd[pos]=t; sl[pos]=c; ++pos; lin[pos]=ta[t]; ta[t]=pos; sd[pos]=s; sl[pos]=0; } int nn; int h[maxn]; int vh[maxn]; int aug(int v,int deta) { if (v==nn) return deta; int mins=nn-1,augc,bak=deta; for (int i=ta[v];i;i=lin[i]) if (sl[i]) { if (h[v]==h[sd[i]]+1) { augc=min(deta,sl[i]); augc=aug(sd[i],augc); deta-=augc; sl[i]-=augc; sl[(i+1^1)-1]+=augc; if (!deta) break; if (h[nn-1]>=nn) return bak-deta; } mins=min(mins,h[sd[i]]); } if (bak==deta) { vh[h[v]]--; if (vh[h[v]]==0) h[nn-1]=nn; h[v]=mins+1; vh[h[v]]++; } return bak-deta; } int n,m; int rd[201],cd[201]; int pipel[maxm]; int main(){ scanf("%d%d",&n,&m); int u,v,l,c; for (int i=1;i<=m;i++) { scanf("%d%d%d%d",&u,&v,&l,&c); pipel[i]=l; rd[v]+=l; cd[u]+=l; biu(u,v,c-l); } nn=n+2;//-- LL sum=0; for (int i=1;i<=n;i++) { if (rd[i]) biu(nn-1,i,rd[i]); if (cd[i]) biu(i,nn,cd[i]); sum+=rd[i]; } LL flow=0; while (h[nn-1]<nn) flow+=aug(nn-1,0x7fffffff); if (sum!=flow) puts("NO "); else { puts("YES"); for (int i=1;i<=m;i++) printf("%d ",pipel[i]+sl[i*2]); } return 0; }
(提示:如果第7个点WA了,说明你没开long long或者数组范围没够。)
Regionals 2013 - Europe - Southwestern - It Can Be Arranged (有源汇的上下界网络流最小流):
大致思路是先弄成无源汇,然后跑一遍maxflow,这样就能保证下界是满足的了。并且因为刚好是满足下界的,所以这样的网络流是最小流。这个最小流指的是在网络中流动的,而不是说的从源点到汇点的最小流。
这题要注意的是,连边(汇点,源点)后,虽能算出源点到汇点的可行流,但它不是最小流。原因是在还没有充分利用网络中的边时,就使用了(汇点,源点)这条边,于是就导致答案偏大。(这里有个很形象的图)
解决方法是先跑一遍maxflow,使之充分利用网络中的边,然后再加上(汇点,源点)这条边,再跑一遍maxflow,就求出了最小流。
这道题因为无上界,必然有可行流,所以就免了上一道题sigma(度数)==flow的判断。
#include <cstdlib> #include <cstdio> #include <algorithm> #include <iostream> #include <cstring> #include <bits/stdc++.h> #define debug(x) cout<<#x<<" = "<<x<<endl #define FOR(i,l,r) for (int i=(l);i<=(r);i++) #define FORD(i,r,l) for (int i=(r);i>=(l);i--) using namespace std; const int maxn=205+1,maxm=120000,jx=0x7fffffff; int pos,ta[maxn],lin[maxm],sd[maxm],sl[maxm]; void biu(int s,int t,int c) { ++pos; lin[pos]=ta[s]; ta[s]=pos; sd[pos]=t; sl[pos]=c; ++pos; lin[pos]=ta[t]; ta[t]=pos; sd[pos]=s; sl[pos]=0; } int nn; int h[maxn]; int vh[maxn]; int aug(int v,int deta) { if (v==nn) return deta; int mins=nn-1,augc,bak=deta; for (int i=ta[v];i;i=lin[i]) if (sl[i]) { if (h[v]==h[sd[i]]+1) { augc=min(deta,sl[i]); augc=aug(sd[i],augc); deta-=augc; sl[i]-=augc; sl[(i+1^1)-1]+=augc; if (!deta) break; if (h[nn-1]>=nn) return bak-deta; } mins=min(mins,h[sd[i]]); } if (bak==deta) { vh[h[v]]--; if (vh[h[v]]==0) h[nn-1]=nn; h[v]=mins+1; vh[h[v]]++; } return bak-deta; } int n,m; int A[101],B[101],S[101]; int clean[101][101]; int rd[101*2],cd[101*2]; void init(int nn) { pos=0; for (int i=0;i<=nn;i++) ta[i]=h[i]=vh[i]=0; for (int i=1;i<=n*2;i++) rd[i+n]=cd[i]=0; } void make_graph() { init(n*2+4); for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) if (B[i]+clean[i][j]<A[j])//? biu(i+n,j,jx); nn=n*2+2; for (int i=1;i<=n;i++) biu(nn-1,i,jx),biu(i+n,nn,jx); for (int i=1;i<=n;i++) { rd[i+n]+=(S[i]-1)/m+1; cd[i]+=(S[i]-1)/m+1; biu(i,i+n,jx); } nn+=2; for (int i=1;i<=n;i++) biu(i,nn,cd[i]),biu(nn-1,i+n,rd[i+n]); } int solve() { int sum=0,flow=0; make_graph(); while (h[nn-1]<nn) flow+=aug(nn-1,jx); for (int i=1;i<=n;i++) sum+=cd[i]; if (sum!=flow) { memset(h,0,sizeof(h)); memset(vh,0,sizeof(vh)); biu(nn-2,nn-3,jx); while (h[nn-1]<nn) aug(nn-1,jx); } int ans=0; nn-=2; for (int i=ta[nn-1];i;i=lin[i]) if (sd[i]==nn) { ans=sl[i]; break; } return ans; } int main(){ for (int T;scanf("%d",&T)!=EOF;) for (int tt=1;T--;tt++) { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%d%d%d",A+i,B+i,S+i); for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) scanf("%d",&clean[i][j]); printf("Case %d: %d ",tt,solve()); } return 0; }
zoj 3229 (有源汇的上下界网络流最大流):
这种题与上面两种题型的不同在于在判断是否存在可行流之后,我们要删除超级汇点和超级源点,还要把(汇点,源点)这条边删去,再从源点到汇点进行一次maxflow。
这题建图很好想。比较坑的是输出的地方描述不好懂。
#include <bits/stdc++.h> #define debug(x) cout<<#x<<" = "<<x<<endl #define FOR(i,l,r) for (int i=(l);i<=(r);i++) #define FORD(i,r,l) for (int i=(r);i>=(l);i--) using namespace std; const int maxn=1400,maxm=160000,jx=0x7fffffff; int pos,ta[maxn],lin[maxm],sd[maxm],sl[maxm]; void biu(int s,int t,int c) { ++pos; lin[pos]=ta[s]; ta[s]=pos; sd[pos]=t; sl[pos]=c; ++pos; lin[pos]=ta[t]; ta[t]=pos; sd[pos]=s; sl[pos]=0; } int nn; int h[maxn]; int vh[maxn]; int aug(int v,int deta) { if (v==nn) return deta; int mins=nn-1,augc,bak=deta; for (int i=ta[v];i;i=lin[i]) if (sl[i]) { if (h[v]==h[sd[i]]+1) { augc=min(deta,sl[i]); augc=aug(sd[i],augc); deta-=augc; sl[i]-=augc; sl[(i+1^1)-1]+=augc; if (!deta) break; if (h[nn-1]>=nn) return bak-deta; } mins=min(mins,h[sd[i]]); } if (bak==deta) { vh[h[v]]--; if (vh[h[v]]==0) h[nn-1]=nn; h[v]=mins+1; vh[h[v]]++; } return bak-deta; } int n,m; int G[maxn]; int rd[maxn],cd[maxn]; int sum; int gra[maxn][maxn]; bool id[maxn][maxn]; void init() { //init pos=0; for (int i=1;i<=n+m+4;i++) ta[i]=h[i]=vh[i]=rd[i]=cd[i]=0; for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) gra[i][j]=id[i][j]=0; //make graph nn=n+m+2; for (int i=1;i<=m;i++) { scanf("%d",G+i); rd[nn]+=G[i]; cd[n+i]+=G[i]; biu(i+n,nn,jx); } int c,d,t,l,r; for (int i=1;i<=n;i++) { scanf("%d%d",&c,&d); biu(nn-1,i,d); for (int j=1;j<=c;j++) { scanf("%d%d%d",&t,&l,&r); t++; cd[i]+=l; rd[t+n]+=l; biu(i,t+n,r-l); gra[i][t]+=l; id[i][t]=1; } } nn+=2; sum=0; for (int i=1;i<=n+m+2;i++) if (cd[i]>rd[i]) biu(i,nn,cd[i]-rd[i]),sum+=cd[i]-rd[i]; else if (rd[i]>cd[i]) biu(nn-1,i,rd[i]-cd[i]); biu(nn-2,nn-3,jx);//make cycle } void solve() { nn-=2; int flow=sl[ta[nn-1]]; ta[nn]=lin[ta[nn]]; ta[nn-1]=lin[ta[nn-1]]; for (int i=1;i<=nn;i++) h[i]=vh[i]=0; while (h[nn-1]<nn) flow+=aug(nn-1,jx); printf("%d ",flow); for (int v=n+1;v<=n+m;v++) for (int i=ta[v];i;i=lin[i]) if (sd[i]>=1&&sd[i]<=n) gra[sd[i]][v-n]+=sl[i]; for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) if (id[i][j]) printf("%d ",gra[i][j]); } int main(){ for (;scanf("%d%d",&n,&m)!=EOF;) { init(); int flow=0; while (h[nn-1]<nn) flow+=aug(nn-1,jx); if (flow!=sum) puts("-1"); else solve(); puts(""); } return 0; }
——至此,上下界网络流题型算是补完了。
二、01分数规划:
poj 3155 Hard Life(最大密度子图):
这是道坑题...把二分的精度从1e-8调到1e-6就能AC了...个中原因还需研究一下...
//13395616 ooyyloo 3155 Accepted 840K 282MS G++ 2963B 2014-08-30 12:33:29 #include <cstdio> #include <iostream> using namespace std; const int maxn=11002+1,maxm=120000; const double inf=1000000.0; const double eps=1e-18; int pos,ta[maxn],lin[maxm],sd[maxm]; double sl[maxm]; void biu(int s,int t,double c) { ++pos; lin[pos]=ta[s]; ta[s]=pos; sd[pos]=t; sl[pos]=c; ++pos; lin[pos]=ta[t]; ta[t]=pos; sd[pos]=s; sl[pos]=0; } int nn; int h[maxn],vh[maxn]; double aug(int v,double deta) { if (v==nn) return deta; int mins=nn-1; double augc,bak=deta; for (int i=ta[v];i;i=lin[i]) if (sl[i]>0.0) { if (h[v]==h[sd[i]]+1) { augc=min(deta,sl[i]); augc=aug(sd[i],augc); deta-=augc; sl[i]-=augc; sl[(i+1^1)-1]+=augc; if (deta<=0) break; if (h[nn-1]>=nn) return bak-deta; } mins=min(mins,h[sd[i]]); } if (bak==deta) { vh[h[v]]--; if (vh[h[v]]==0) h[nn-1]=nn; h[v]=mins+1; vh[h[v]]++; } return bak-deta; } int n,m; int edge[1001][2]; bool vis[maxn]; void init() { pos=0; for (int i=1;i<=nn;i++) ta[i]=h[i]=vh[i]=0; } void make_graph(double x) { nn=n+m+2; init(); for (int i=1;i<=m;i++) { biu(nn-1,i,1.0); biu(i,edge[i][0]+m,inf); biu(i,edge[i][1]+m,inf); } for (int i=1;i<=n;i++) biu(i+m,nn,x); } int ans; void dfs(int v) { vis[v]=1; if (v>m&&v<=m+n) ans++; for (int i=ta[v];i;i=lin[i]) if (sl[i]>0) if (!vis[sd[i]]) dfs(sd[i]); } void solve() { /*double flow=0; make_graph(1.25); while (h[nn-1]<nn) flow+=aug(nn-1,inf); cout<<flow<<endl;*/ double l=0,r=m,mid,gap=1.0/n/n/10; double flow; while (r-l>=1e-6) { mid=(l+r)/2; flow=m; make_graph(mid); while (h[nn-1]<nn) flow-=aug(nn-1,inf); if (flow==0.0) r=mid; else l=mid; } make_graph(l); while (h[nn-1]<nn) aug(nn-1,inf); ans=0; for (int i=1;i<=nn;i++) vis[i]=0; dfs(nn-1); /*for (int i=ta[nn];i;i=lin[i]) if (sl[i]==l)//? if (!vis[sd[i]-m]) vis[sd[i]-m]=1,ans++;*/ printf("%d ",ans); for (int i=1;i<=n;i++) if (vis[i+m]) printf("%d ",i); } int main(){ for (;scanf("%d%d",&n,&m)!=EOF;) { for (int i=1;i<=m;i++) scanf("%d%d",&edge[i][0],&edge[i][1]); if (!m) printf("1 1 "); else solve(); } return 0; }