1「網絡流 24 題」搭配飛行員
不說了,妥妥的最大流...

#include<bits/stdc++.h> #define ll long long using namespace std; const int N=110; int link[N],tot,n,m,vis[N],match[N]; struct edge{int y,next;}a[N*N]; inline int read() { int x=0,ff=1; char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*ff; } inline void add(int x,int y) { a[++tot].y=y; a[tot].next=link[x]; link[x]=tot; } inline bool dfs(int x) { for(int i=link[x];i;i=a[i].next) { int y=a[i].y; if(!vis[y]) { vis[y]=1; if(!match[y]||dfs(match[y])) {match[y]=x;return true;} } } return false; } int main() { //freopen("1.in","r",stdin); n=read();m=read(); int x,y; while(cin>>x>>y) { add(x,y); } int ans=0; for(int i=1;i<=m;++i) { memset(vis,0,sizeof(vis)); if(dfs(i)) ans++; } printf("%d",ans); return 0; }
2「網絡流 24 題」太空飛行計划
好題,想了很長時間,都沒得答案,最后才知道自己根本沒學過這知識....
首先我們先簡化題意:每個實驗依賴某些儀器,每個實驗有正的權值,每個儀器有負的權值,要求選出最大權值的方案.可以算是帶權閉合圖的模板了.我們將實驗向他依賴的儀器連邊,那么每個合法的方案就是一個閉合圖(選出若干個點構成一個點集,使得每個點出邊指向的點都在點集中).所以什么依賴什么就可以用閉合圖寫.
之后題目要求我們選出最大權值閉合圖.我們構造出一個網絡.將s連向所有正的點,容量為權值,所有負的點連向t,容量為權值的絕對值.之后根據依賴關系連邊,容量為INF.結論:最小割后,s所在的集合即為最大帶權閉合圖.
證明:點這里
怎么說呢,這里說的簡單一點,由於中間的容量都為INF,所以不可能被割斷.所以最小割一定是簡單割(割集的每一條邊都與s或t有關.)而簡單割一定對應一個閉合圖(顯然...)之后我們設x1為s集合中的正的點權值和,x2為負的點權值和.y1為t集合中的正的點權值和,y2為負的點權值和.
之后我們定義一個簡單割的容量為C,則C=x2+y1.之后我們考慮N集合中的閉合圖權值和為W=x1-x2,於是C+W=x2+y1+x1-x2=x1+y1=所有正的點權值和(我們設為tot).於是C+W=tot.W=tot-C,要是得W盡量大,我們可以將C盡量小.所以跑最小割沒毛病...證畢.

#include<bits/stdc++.h> #define ll long long using namespace std; const int N=110,INF=1e9; int n,m,s,t,link[N],tot=1,d[N],current[N]; struct edge{int y,v,next;}a[N*N*2]; inline int read() { int x=0,ff=1; char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*ff; } inline void add(int x,int y,int v) { a[++tot].y=y;a[tot].v=v;a[tot].next=link[x];link[x]=tot; a[++tot].y=x;a[tot].v=0;a[tot].next=link[y];link[y]=tot; } inline bool bfs() { queue<int>q;q.push(s); memset(d,0,sizeof(d)); memcpy(current,link,sizeof(current)); d[s]=1; while(!q.empty()) { int x=q.front();q.pop(); for(int i=link[x];i;i=a[i].next) { int y=a[i].y; if(d[y]||!a[i].v) continue; d[y]=d[x]+1; q.push(y); if(y==t) return true; } } return false; } inline int dinic(int x,int flow) { if(x==t) return flow; int rest=flow,k; for(int i=current[x];i&&rest;i=a[i].next) { current[x]=i; int y=a[i].y; if(d[y]==d[x]+1&&a[i].v) { k=dinic(y,min(rest,a[i].v)); if(!k) d[y]=-1; a[i].v-=k; a[i^1].v+=k; rest-=k; } } return flow-rest; } inline void work() { queue<int>q;q.push(s); memset(d,0,sizeof(d)); d[s]=1; while(!q.empty()) { int x=q.front();q.pop(); for(int i=link[x];i;i=a[i].next) { int y=a[i].y; if(d[y]||!a[i].v) continue; d[y]=1;q.push(y); } } } int main() { freopen("1.in","r",stdin); m=read();n=read(); s=0;t=n+m+1; int ans=0; for(int i=1;i<=m;++i) { int x;scanf("%d",&x); ans+=x; add(s,i,x); while(getchar()==' ') scanf("%d",&x),add(i,x+m,INF); } for(int i=1;i<=n;++i) { int x=read(); add(i+m,t,x); } int maxflow=0,flow; while(bfs()) while(flow=dinic(s,INF)) maxflow+=flow; work(); for(int i=1;i<=m;++i) if(d[i]) printf("%d ",i);puts(""); for(int i=1;i<=n;++i) if(d[i+m]) printf("%d ",i);puts(""); printf("%d",ans-maxflow); return 0; }
3「網絡流 24 題」最小路徑覆蓋
這個算法進階上有詳細講解,就不多說了,簡單而言就是將每個店拆成出點與入點,構成二分圖,最后跑最大匹配即可...

#include<bits/stdc++.h> #define ll long long using namespace std; const int N=410,M=6010; int link[N],tot,matchx[N],matchy[N],n,m,vis[N],ans; struct edge{int y,next;}a[M]; inline int read() { int x=0,ff=1; char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*ff; } inline void add(int x,int y) { a[++tot].y=y; a[tot].next=link[x]; link[x]=tot; } inline bool dfs(int x) { for(int i=link[x];i;i=a[i].next) { int y=a[i].y; if(!vis[y]) { vis[y]=1; if(!matchy[y]||dfs(matchy[y])) {matchx[x]=y;matchy[y]=x;return true;} } } return false; } inline void dfs1(int x) { if(matchx[x]) { cout<<matchx[x]-n<<' '; vis[matchx[x]-n]=1; dfs1(matchx[x]-n); } } int main() { freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<=m;++i) { int x=read(),y=read(); add(x,y+n); } for(int i=1;i<=n;++i) { memset(vis,0,sizeof(vis)); if(dfs(i)) ++ans; } memset(vis,0,sizeof(vis)); // dfs1(1); for(int i=1;i<=n;++i) { if(matchx[i]&&!vis[i]) { cout<<i<<' '; vis[i]=1; dfs1(i); puts(""); } } printf("%d\n",n-ans); return 0; }
4「網絡流 24 題」魔術球
這種題就是如果不看題解,死活想不出來的題.....
我們分析題目發現這個題好像和桿子沒有關系,因為全部都是球的操作,而且每一個桿子都是一串球的編號,這就啟示我們將球與球之間連邊.
每一條路徑就是一個桿子,想到這里了我們就可以借用二分的思路,以此增加球的個數,判斷桿子的數量是否符合答案.至於桿子的數量,考慮這是一個DAG,而且一個桿子就是一個路徑,要求將所有的點都覆蓋,那不就是最小路徑覆蓋嗎?好了,就這吧....

#include<bits/stdc++.h> #define ll long long using namespace std; const int N=10010; int link[N],tot,n,id,matchx[N],matchy[N],vis[N],now; struct edge{int y,next;}a[N*100]; inline int read() { int x=0,ff=1; char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*ff; } inline void add(int x,int y) { a[++tot].y=y; a[tot].next=link[x]; link[x]=tot; } inline bool dfs(int x) { for(int i=link[x];i;i=a[i].next) { int y=a[i].y; if(vis[y]!=id) { vis[y]=id; if(!matchy[y]||dfs(matchy[y])) {matchx[x]=y;matchy[y]=x;return true;} } } return false; } inline void dfs1(int x) { int y=matchx[x]; if(y) { cout<<y<<' '; vis[y]=1; dfs1(y); } } int main() { freopen("1.in","r",stdin); n=read();now=0; int data; for(data=1;;data++) { for(int i=1;i<data;++i) if((int)sqrt(i+data)*(int)sqrt(i+data)==i+data) add(i,data); for(int i=1;i<=data;++i) { if(matchx[i]) continue; ++id; if(dfs(i)) ++now; } if(data-now>n) break; } //for(int i=1;i<11;++i) cout<<matchx[i]<<endl; printf("%d\n",data-1); memset(vis,0,sizeof(vis)); for(int i=1;i<data;++i) { if(matchx[i]&&!vis[i]) { vis[i]=1; cout<<i<<' '; dfs1(i); puts(""); } } for(int i=1;i<data;++i) if(!vis[i]) cout<<i<<endl; return 0; }
5「網絡流 24 題」圓桌聚餐
很典型的多重匹配問題,每一個地區的每一個人必須與一個餐桌進行匹配,由於一個地區的代表不能在一個餐桌上,所以一個地區向每一個餐桌連邊的容量為1.最后檢查最大流是否與總人數相等即可.

#include<bits/stdc++.h> #define ll long long using namespace std; const int N=430,INF=1e9; int m,n,link[N],tot=1,s,t,d[N],current[N]; struct edge{int y,next,v;}a[N*N*2]; inline int read() { int x=0,ff=1; char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*ff; } inline void add(int x,int y,int v) { a[++tot].y=y;a[tot].v=v;a[tot].next=link[x];link[x]=tot; a[++tot].y=x;a[tot].v=0;a[tot].next=link[y];link[y]=tot; } inline bool bfs() { queue<int>q;q.push(s); memset(d,0,sizeof(d)); memcpy(current,link,sizeof(current)); d[s]=1; while(!q.empty()) { int x=q.front();q.pop(); for(int i=link[x];i;i=a[i].next) { int y=a[i].y; if(d[y]||!a[i].v) continue; d[y]=d[x]+1; q.push(y); if(y==t) return true; } } return false; } inline int dinic(int x,int flow) { if(x==t) return flow; int rest=flow,k; for(int i=current[x];i&&rest;i=a[i].next) { current[x]=i; int y=a[i].y; if(d[y]==d[x]+1&&a[i].v) { k=dinic(y,min(rest,a[i].v)); if(!k) d[y]=-1; a[i].v-=k; a[i^1].v+=k; rest-=k; } } return flow-rest; } int main() { freopen("1.in","r",stdin); m=read();n=read(); s=0;t=n+m+1; int ans=0; for(int i=1;i<=m;++i) { int x=read(); add(s,i,x); ans+=x; } for(int i=1;i<=n;++i) { int x=read(); add(i+m,t,x); } for(int i=1;i<=m;++i) for(int j=1;j<=n;++j) add(i,j+m,1); int maxflow=0,flow; while(bfs()) while(flow=dinic(s,INF)) maxflow+=flow; if(maxflow<ans){printf("0");return 0;} printf("%d\n",1); for(int x=1;x<=m;++x) { for(int i=link[x];i;i=a[i].next) { int y=a[i].y; if(!a[i].v) printf("%d ",y-m); } puts(""); } return 0; }
6最長遞增子序列
怎么說呢,感覺自己的網絡流有刷新了......
這里引入一個分層圖的概念,顧名思義,就是一層一層的圖唄!這主要體現一種思想,在我們建圖時,可以按照題目要求,建立分層圖,使得從s到t就是一條合法路徑...
針對此題來說,就是我們求一個f數組,表示以i開頭的最長遞增子序列.顯然只有f[i]==ans時,這個點才能是一個序列的起點,我哦們將s向這個點連容量為1的邊,之后考慮i的后繼,如果b[i]<=b[j]&&f[i]==f[j]+1,此時j才能是i的后繼.我們將i向j連邊,但同時注意每個店只能用一次,我們拆點限流.即可。第三問,修改與1,n有關的邊的容量即可.

#include<bits/stdc++.h> #define ll long long using namespace std; const int N=510,M=1010,INF=1e9; int n,b[N],f[N],ans,link[M],tot=1,d[M],current[M],s,t; struct edge{int y,next,v;}a[M*M*2]; inline int read() { int x=0,ff=1; char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*ff; } inline void add(int x,int y,int v) { a[++tot].y=y;a[tot].v=v;a[tot].next=link[x];link[x]=tot; a[++tot].y=x;a[tot].v=0;a[tot].next=link[y];link[y]=tot; } inline bool bfs() { queue<int>q;q.push(s); memset(d,0,sizeof(d)); memcpy(current,link,sizeof(current)); d[s]=1; while(!q.empty()) { int x=q.front();q.pop(); for(int i=link[x];i;i=a[i].next) { int y=a[i].y; if(d[y]||!a[i].v) continue; d[y]=d[x]+1; q.push(y); if(y==t) return true; } } return false; } inline int dinic(int x,int flow) { if(x==t) return flow; int rest=flow,k; for(int i=current[x];i&&rest;i=a[i].next) { current[x]=i; int y=a[i].y; if(d[y]==d[x]+1&&a[i].v) { k=dinic(y,min(rest,a[i].v)); a[i].v-=k; a[i^1].v+=k; rest-=k; } } return flow-rest; } int main() { freopen("1.in","r",stdin); n=read(); for(int i=1;i<=n;++i) b[i]=read(); for(int i=n;i>=1;--i) { for(int j=i+1;j<=n;++j) if(b[j]>=b[i]) f[i]=max(f[i],f[j]); f[i]++; ans=max(ans,f[i]); } printf("%d\n",ans); s=0;t=n<<1|1; for(int i=1;i<=n;++i) { if(f[i]==ans) add(s,i,INF); for(int j=i+1;j<=n;++j) if(b[i]<=b[j]&&f[i]==f[j]+1) add(i+n,j,1); if(f[i]==1) add(i+n,t,1); add(i,i+n,1); } int maxflow=0,flow; while(bfs()) while(flow=dinic(s,INF)) maxflow+=flow; printf("%d\n",maxflow); memset(link,0,sizeof(link)); memset(a,0,sizeof(a));tot=1; if(n==1) {puts("1");return 0;} for(int i=1;i<=n;++i) { if(f[i]==ans) { if(i!=1) add(s,i,1); else add(s,1,INF); } for(int j=i+1;j<=n;++j) if(b[i]<=b[j]&&f[i]==f[j]+1) add(i+n,j,1); if(f[i]==1) { if(i!=n) add(i+n,t,1); else add(i+n,t,INF); } if(i!=1&&i!=n) add(i,i+n,1); else add(i,i+n,INF); } maxflow=0; while(bfs()) while(flow=dinic(s,INF)) maxflow+=flow; printf("%d\n",maxflow); return 0; }
7試題庫
傻逼題,題意感覺好懵,結果沒看懂題意,錯想了老長時間了.....
我覺得題意里面應該加一句:每道題只能應用於一個類型.這樣就一目了然了,經典的匹配問題一個問題能匹配多個類型,而每個類型都有限制.那我們直接將類型連匯,容量為限制.問題連類型,容量為1,跑最大流即可.

#include<bits/stdc++.h> #define ll long long using namespace std; const int N=2050,INF=1e9; int link[N],tot=1,d[N],current[N],n,k,s,t; struct edge{int y,v,next;}a[N*N*2]; vector<int>v[N]; inline int read() { int x=0,ff=1; char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*ff; } inline void add(int x,int y,int v) { a[++tot].y=y;a[tot].v=v;a[tot].next=link[x];link[x]=tot; a[++tot].y=x;a[tot].v=0;a[tot].next=link[y];link[y]=tot; } inline bool bfs() { queue<int>q;q.push(s); memset(d,0,sizeof(d)); memcpy(current,link,sizeof(current)); d[s]=1; while(!q.empty()) { int x=q.front();q.pop(); for(int i=link[x];i;i=a[i].next) { int y=a[i].y; if(d[y]||!a[i].v) continue; d[y]=d[x]+1; q.push(y); if(y==t) return true; } } return false; } inline int dinic(int x,int flow) { if(x==t) return flow; int rest=flow,k; for(int i=current[x];i&&rest;i=a[i].next) { current[x]=i; int y=a[i].y; if(d[y]==d[x]+1&&a[i].v) { k=dinic(y,min(rest,a[i].v)); a[i].v-=k; a[i^1].v+=k; rest-=k; } } return flow-rest; } int main() { //freopen("1.in","r",stdin); k=read();n=read(); s=0;t=n+k+1; int ans=0; for(int i=1;i<=k;++i) { int x=read(); ans+=x; add(i+n,t,x); } for(int i=1;i<=n;++i) { add(s,i,1); int z=read(); for(int j=1;j<=z;++j) { int x=read(); add(i,x+n,1); } } int maxflow=0,flow; while(bfs()) while(flow=dinic(s,INF)) maxflow+=flow; if(maxflow<ans) puts("No Solution!"); else { for(int i=1;i<=n;++i) for(int j=link[i];j;j=a[j].next) { int y=a[j].y; if(y-n<=0) continue; if(!a[j].v) v[y-n].push_back(i); } for(int i=1;i<=k;++i) { printf("%d: ",i); for(int j=0;j<v[i].size();++j) printf("%d ",v[i][j]); puts(""); } } return 0; }
8軟件補丁
狗屁網絡流,一直想怎么建圖,最后還是用dij搞得....
直接將狀態壓縮,連邊跑dij即可.

#include<bits/stdc++.h> #define ll long long using namespace std; const int N=1e6+1e5,M=110; int link[N],tot,n,m,B1[M],B2[M],F1[M],F2[M],t[M],vis[N]; ll dis[N]; struct edge{int y,v,next;}a[N*40]; char c[100]; inline int read() { int x=0,ff=1; char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*ff; } inline void add(int x,int y,int v) { a[++tot].y=y; a[tot].v=v; a[tot].next=link[x]; link[x]=tot; } inline void dij() { priority_queue<pair<ll,int> >q; memset(vis,0,sizeof(vis)); for(int i=0;i<1<<n;++i) dis[i]=1e18; q.push({0,(1<<n)-1}); dis[(1<<n)-1]=0; while(!q.empty()) { int x=q.top().second;q.pop(); if(vis[x]) continue; vis[x]=1; for(int i=link[x];i;i=a[i].next) { int y=a[i].y; if(dis[y]>dis[x]+(ll)a[i].v) { dis[y]=dis[x]+(ll)a[i].v; q.push({-dis[y],y}); } } } } int main() { freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<=m;++i) { t[i]=read(); scanf("%s",c+1); for(int j=1;j<=n;++j) { B1[i]<<=1; B2[i]<<=1; if(c[j]=='+') B1[i]|=1; else if(c[j]=='-') B2[i]|=1; } scanf("%s",c+1); for(int j=1;j<=n;++j) { F1[i]<<=1; F2[i]<<=1; if(c[j]=='-') F1[i]|=1; else if(c[j]=='+') F2[i]|=1; } } for(int i=0;i<(1<<n);++i) { for(int j=1;j<=m;++j) { if((B1[j]&i)!=B1[j]||(B2[j]&i)) continue; int s=i&(~F1[j])|F2[j]; add(i,s,t[j]); } } dij(); printf("%d",dis[0]==1e18?0:dis[0]); return 0; }
9數字梯形
感覺和方格取數一模一樣......
直接構造分層圖即可..

#include<bits/stdc++.h> #define ll long long using namespace std; const int N=3200,M=40,INF=1e9; int link[N],tot=1,m,n,c[M][M],s,t,id[M][M],cnt,ans; int incf[N],pre[N],dis[N],vis[N]; struct edge{int y,next,v,c;}a[N*N]; inline int read() { int x=0,ff=1; char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*ff; } inline void add(int x,int y,int v,int c) { a[++tot].y=y;a[tot].c=c;a[tot].v=v;a[tot].next=link[x];link[x]=tot; a[++tot].y=x;a[tot].c=-c;a[tot].v=0;a[tot].next=link[y];link[y]=tot; } inline bool spfa() { queue<int>q;q.push(s); memset(dis,0xef,sizeof(dis)); memset(vis,0,sizeof(vis)); dis[s]=0;vis[s]=1;incf[s]=INF; while(!q.empty()) { int x=q.front();q.pop();vis[x]=0; for(int i=link[x];i;i=a[i].next) { int y=a[i].y; if(!a[i].v) continue; if(dis[y]<dis[x]+a[i].c) { dis[y]=dis[x]+a[i].c; incf[y]=min(incf[x],a[i].v); pre[y]=i; if(!vis[y]) vis[y]=1,q.push(y); } } } if(dis[t]==0xefefefef) return false; return true; } inline void updata() { int x=t; while(x!=s) { int i=pre[x]; a[i].v-=incf[t]; a[i^1].v+=incf[t]; x=a[i^1].y; } ans+=dis[t]*incf[t]; } inline void subtask1() { s=0;t=cnt*2+1; for(int i=1;i<=n;++i) for(int j=1;j<=m+i-1;++j) { add(id[i][j],id[i][j]+cnt,1,c[i][j]); if(i!=n) { add(id[i][j]+cnt,id[i+1][j],1,0); add(id[i][j]+cnt,id[i+1][j+1],1,0); } else if(i==n) add(id[i][j]+cnt,t,1,0); if(i==1) add(s,id[i][j],1,0); } while(spfa()) updata(); printf("%d\n",ans); } inline void subtask2() { memset(a,0,sizeof(a));tot=1; memset(link,0,sizeof(link)); s=0;t=cnt+1; for(int i=1;i<=n;++i) for(int j=1;j<=m+i-1;++j) { if(i!=n) { add(id[i][j],id[i+1][j],1,c[i+1][j]); add(id[i][j],id[i+1][j+1],1,c[i+1][j+1]); } else if(i==n) add(id[i][j],t,INF,0); if(i==1) add(s,id[i][j],1,c[i][j]); } ans=0; while(spfa()) updata(); printf("%d\n",ans); } inline void subtask3() { memset(a,0,sizeof(a));tot=1; memset(link,0,sizeof(link)); s=0;t=cnt+1; for(int i=1;i<=n;++i) for(int j=1;j<=m+i-1;++j) { if(i!=n) { add(id[i][j],id[i+1][j],INF,c[i+1][j]); add(id[i][j],id[i+1][j+1],INF,c[i+1][j+1]); } else if(i==n) add(id[i][j],t,INF,0); if(i==1) add(s,id[i][j],1,c[i][j]); } ans=0; while(spfa()) updata(); printf("%d\n",ans); } int main() { freopen("1.in","r",stdin); m=read();n=read(); for(int i=1;i<=n;++i) for(int j=1;j<=m+i-1;++j) c[i][j]=read(),id[i][j]=++cnt; subtask1(); subtask2(); subtask3(); return 0; }
10運輸問題
妥妥的費用流就不多說了....

#include<bits/stdc++.h> #define ll long long using namespace std; const int N=210,INF=1e9; int m,n,s,t,link[N],tot=1,dis[N],incf[N],pre[N],vis[N],ans,st[N],ma[N],b[N][N]; struct edge{int y,v,next,c;}a[N*N*2]; inline int read() { int x=0,ff=1; char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*ff; } inline void add(int x,int y,int v,int c) { a[++tot].y=y;a[tot].v=v;a[tot].c=c;a[tot].next=link[x];link[x]=tot; a[++tot].y=x;a[tot].v=0;a[tot].c=-c;a[tot].next=link[y];link[y]=tot; } inline void init() { for(int i=1;i<=m;++i) st[i]=read(); for(int i=1;i<=n;++i) ma[i]=read(); for(int i=1;i<=m;++i) for(int j=1;j<=n;++j) b[i][j]=read(); } inline void prework() { memset(a,0,sizeof(a));tot=1; memset(link,0,sizeof(link)); for(int i=1;i<=m;++i) add(s,i,st[i],0); for(int i=1;i<=n;++i) add(i+m,t,ma[i],0); for(int i=1;i<=m;++i) for(int j=1;j<=n;++j) add(i,j+m,INF,b[i][j]); } inline bool spfa(int op) { queue<int>q;q.push(s); memset(vis,0,sizeof(vis)); if(op==1) memset(dis,0x3f,sizeof(dis)); else memset(dis,0xef,sizeof(dis)); dis[s]=0;vis[s]=1;incf[s]=INF; while(!q.empty()) { int x=q.front();q.pop();vis[x]=0; for(int i=link[x];i;i=a[i].next) { int y=a[i].y; if(!a[i].v) continue; if(op==1) { if(dis[y]>dis[x]+a[i].c) { dis[y]=dis[x]+a[i].c; incf[y]=min(incf[x],a[i].v); pre[y]=i; if(!vis[y]) vis[y]=1,q.push(y); } } else { if(dis[y]<dis[x]+a[i].c) { dis[y]=dis[x]+a[i].c; incf[y]=min(incf[x],a[i].v); pre[y]=i; if(!vis[y]) vis[y]=1,q.push(y); } } } } if(op==1&&dis[t]==0x3f3f3f3f) return false; else if(op==2&&dis[t]==0xefefefef) return false; return true; } inline void updata() { int x=t; while(x!=s) { int i=pre[x]; a[i].v-=incf[t]; a[i^1].v+=incf[t]; x=a[i^1].y; } ans+=dis[t]*incf[t]; } int main() { freopen("1.in","r",stdin); m=read();n=read(); s=0;t=m+n+1; init(); prework();ans=0; while(spfa(1)) updata(); printf("%d\n",ans); prework();ans=0; while(spfa(2)) updata(); printf("%d\n",ans); return 0; }
11最長 k 可重區間集
似乎一看題目便知道要搞什么幺兒子..
首先我們可以發現因為每個點的最多覆蓋限制為k,所以我們想到如果選一條線段就將線段上的所有的點覆蓋次數減1。
之后我們考慮用網絡流做這道題.(網絡流擅長做各種限流題...)我們還可以想象成匹配,每個點最多與k個線段匹配.可是如果選一條線段的話,我們需要將這個區間都覆蓋一遍.這個是就是典型的一流對多流的問題.
一般的做法是將需要區間覆蓋的東西串聯起來.如果選線段的話,就從起點到終點連邊.這是常規套路.
之后我們考慮在這道題上如何做.
我們將所有的端點離散化,串聯起來.考慮每個點都有一個k的限制我們如果在這個網絡里體現。
對於限制,一般都與s或t有關。我們假設將限制與t建立起聯系。也就是每個點都向t連一條容量為k的邊.這時我們思考如果選一條線段如何統計答案。顯然如果我們選一條線段需要將區間中的每個點都流1的流,這楊統計答案.可這樣顯然很難實現..
那我們將限制與s相關聯。我們從源點向第一個點連容量為k的邊.表示你最多這么多流.之后我們只要考慮將區間都少流1的流即可.那我們直接從起點到終點連容量為1的邊即可.費用為長度.考慮不用線段的話,相當於這個點到下一個點沒有阻礙,所以我們直接從每一個點都想下一個點連容量為k的邊就行.這里注意從源點到匯點的最大流一定是k,因為途中並沒有流的丟失...所以我們直接跑最大流最大費用即可.

#include<bits/stdc++.h> #define ll long long using namespace std; const int N=1010,INF=1e9; int link[N],tot=1,s,t,n,k,b[N],cnt; int ans,dis[N],vis[N],incf[N],pre[N]; struct node{int x,y;}d[N],raw[N]; struct edge{int y,v,c,next;}a[N*N*2]; inline int read() { int x=0,ff=1; char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*ff; } inline int find(int x){return lower_bound(b+1,b+cnt+1,x)-b;} inline void add(int x,int y,int v,int c) { a[++tot].y=y;a[tot].c=c;a[tot].v=v;a[tot].next=link[x];link[x]=tot; a[++tot].y=x;a[tot].c=-c;a[tot].v=0;a[tot].next=link[y];link[y]=tot; } inline bool spfa() { queue<int>q;q.push(s); memset(dis,0xef,sizeof(dis)); memset(vis,0,sizeof(vis)); dis[s]=0;vis[s]=1;incf[s]=INF; while(!q.empty()) { int x=q.front();q.pop();vis[x]=0; for(int i=link[x];i;i=a[i].next) { int y=a[i].y; if(!a[i].v) continue; if(dis[y]<dis[x]+a[i].c) { dis[y]=dis[x]+a[i].c; incf[y]=min(incf[x],a[i].v); pre[y]=i; if(!vis[y]) vis[y]=1,q.push(y); } } } if(dis[t]==0xefefefef) return false; return true; } inline void updata() { int x=t; while(x!=s) { int i=pre[x]; a[i].v-=incf[t]; a[i^1].v+=incf[t]; x=a[i^1].y; } ans+=dis[t]*incf[t]; } int main() { freopen("1.in","r",stdin); n=read();k=read(); for(int i=1;i<=n;++i) { d[i].x=read(); d[i].y=read(); b[++cnt]=d[i].x; b[++cnt]=d[i].y; } sort(b+1,b+cnt+1); cnt=unique(b+1,b+cnt+1)-b-1; for(int i=1;i<=n;++i) raw[i].x=find(d[i].x),raw[i].y=find(d[i].y); s=0;t=cnt+1; for(int i=1;i<=cnt;++i) { if(i==cnt) add(i,t,k,0); else { if(i==1) add(s,i,k,0); add(i,i+1,k,0); } for(int j=1;j<=n;++j) if(raw[j].x==i) add(i,raw[j].y,1,d[j].y-d[j].x); } while(spfa()) updata(); printf("%d",ans); return 0; }
例題:
[NOI2008]志願者招募
刁鑽的好題目啊!..
首先我們可以直觀地發現這也是一個典型的一流對多流的題目.
考慮每個志願者我們將從Si到Ti連一條容量為INF,費用為c[i]的邊。之后考慮怎么講每個點的限制加進去.仔細觀察這個題中每個點的限制,我們發現是每個點的必須>=a[i]。由於我們用志願者其實是幫助一個點流出流量的..所以我們可以大致的判斷我們需要每個點從志願者這個途徑流出的量>=a[i]。這樣我們就可從(i,i+1)這條邊做文章了.這樣我們考慮設一個基准,由於網絡流中每個點流量守恆,及流入多少一定流出多少.我們考慮第一個用志願者的日子,加入這個點流入INF,那么我們向下一個點流入INF-a[i],這樣就導致至少有a[i]的流從志願者的途徑流.倘若流入的不是INF,那不就失效了嗎?不,如過流入的不是INF,那說明前面一定分流了。那我們直接將這些流量,從INF-a[i]流出。倘若有的流流不出了,那說明用志願者的太少了.這里我們將志願者用超過容量的流量比作.因為思考我們串聯的本質。
我們是將志願者直接從s連到t的,也就是說他們跨國過的點都是享受到有志願者的感受(爽).考慮他們少了流就是他們享受的志願者.之后考慮我們怎么強制這些點少流流量.有一個辦法就是我們強制給他們塞一些流量。之后他們向下一個點的容量為INF-a[i],這樣他們牛必須通過其他途徑少流這些流量。這樣就是我們的目的也達成了.
之后我們將s與1連邊,n向t連邊...跑最大流即可.

#include<bits/stdc++.h> #define ll long long using namespace std; const int N=1010,INF=1e9; int link[N],tot=1,n,m,s,t,ans; int dis[N],vis[N],pre[N],incf[N]; struct edge{int y,v,next,c;}a[N*N*2]; inline int read() { int x=0,ff=1; char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*ff; } inline void add(int x,int y,int v,int c) { a[++tot].y=y;a[tot].c=c;a[tot].v=v;a[tot].next=link[x];link[x]=tot; a[++tot].y=x;a[tot].c=-c;a[tot].v=0;a[tot].next=link[y];link[y]=tot; } inline bool spfa() { queue<int>q;q.push(s); memset(dis,0x3f,sizeof(dis)); memset(vis,0,sizeof(vis)); dis[s]=0;vis[s]=1;incf[s]=INF; while(!q.empty()) { int x=q.front();q.pop();vis[x]=0; for(int i=link[x];i;i=a[i].next) { int y=a[i].y; if(!a[i].v) continue; if(dis[y]>dis[x]+a[i].c) { dis[y]=dis[x]+a[i].c; incf[y]=min(incf[x],a[i].v); pre[y]=i; if(!vis[y]) vis[y]=1,q.push(y); } } } if(dis[t]==0x3f3f3f3f) return false; return true; } inline void updata() { int x=t; while(x!=s) { int i=pre[x]; a[i].v-=incf[t]; a[i^1].v+=incf[t]; x=a[i^1].y; } ans+=incf[t]*dis[t]; } int main() { freopen("1.in","r",stdin); n=read();m=read(); s=0;t=n+1; for(int i=1;i<=n;++i) { if(i==1) add(s,i,INF,0); int x=read(); add(i,i+1,INF-x,0); } for(int i=1;i<=m;++i) { int x=read(),y=read(),v=read(); add(x,y+1,INF,v); } while(spfa()) updata(); printf("%d",ans); return 0; }
[CTSC1999]家園 / 星際轉移問題
//這道毒瘤網絡流的題目...
//讓我們在打代碼之前先理清思路,爭取一次就過..
//首先有人數,求最短時間,我們第一思路就是費用流,但轉念一想,費用流對應的是一流一代價,但題目的要求確是一艘飛船
//可以載h[i]個人,但帶來的代價確實1,也就是說多個人才造成這1的代價,這不符合費用流的設定,所以我們暫時將費用流
//pass掉...那接着考慮,我們發現這個題目的模型求人數非常符合最大流的設定,但時間就是我們要思考的問題.
//之后我們又發現數據貌似很小,(嗯,你沒看錯).這啟示我們枚舉答案, 用最大流來判斷答案是否可靠..
//接下來我們按照時間順序搞事情,直到當前的月球上有超過k個人為止...
//t時刻.我們可以計算出每一艘飛船下一站目的地並連邊,且將每一個空間站t-1的時刻向t時刻連邊.
//另外如果下一站是地球的話,我們在另用s向它連容量為INF的邊,如果是月球的話,我們從他另向t連容量為t的邊.

//這道毒瘤網絡流的題目... //讓我們在打代碼之前先理清思路,爭取一次就過.. //首先有人數,求最短時間,我們第一思路就是費用流,但轉念一想,費用流對應的是一流一代價,但題目的要求確是一艘飛船 //可以載h[i]個人,但帶來的代價確實1,也就是說多個人才造成這1的代價,這不符合費用流的設定,所以我們暫時將費用流 //pass掉...那接着考慮,我們發現這個題目的模型求人數非常符合最大流的設定,但時間就是我們要思考的問題. //之后我們又發現數據貌似很小,(嗯,你沒看錯).這啟示我們枚舉答案, 用最大流來判斷答案是否可靠.. //接下來我們按照時間順序搞事情,直到當前的月球上有超過k個人為止... //t時刻.我們可以計算出每一艘飛船下一站目的地並連邊,且將每一個空間站t-1的時刻向t時刻連邊. //另外如果下一站是地球的話,我們在另用s向它連容量為INF的邊,如果是月球的話,我們從他另向t連容量為t的邊. #include<bits/stdc++.h> #define ll long long using namespace std; const int N=14,M=21,INF=1e9,maxn=2e5+10; int link[maxn],tot=1,d[maxn],current[maxn],s,t; int n,m,k,r[M],h[M],c[M][N],id[N+2][300010],f[N]; struct edge{int y,v,next;}a[maxn*60]; inline int read() { int x=0,ff=1; char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*ff; } inline void add(int x,int y,int v) { a[++tot].y=y;a[tot].v=v;a[tot].next=link[x];link[x]=tot; a[++tot].y=x;a[tot].v=0;a[tot].next=link[y];link[y]=tot; } inline int getf(int k){return f[k]==k?k:f[k]=getf(f[k]);} inline void merge(int x,int y) { int t1=getf(x),t2=getf(y); if(t1!=t2) f[t1]=t2; } inline bool bfs() { queue<int>q;q.push(s); memset(d,0,sizeof(d)); memcpy(current,link,sizeof(current)); d[s]=1; while(!q.empty()) { int x=q.front();q.pop(); for(int i=link[x];i;i=a[i].next) { int y=a[i].y; if(d[y]||!a[i].v) continue; d[y]=d[x]+1; q.push(y); if(y==t) return true; } } return false; } inline int dinic(int x,int flow) { if(x==t) return flow; int rest=flow,k; for(int i=current[x];i&&rest;i=a[i].next) { current[x]=i; int y=a[i].y; if(d[y]==d[x]+1&&a[i].v) { k=dinic(y,min(rest,a[i].v)); if(!k) d[y]=-1; a[i].v-=k; a[i^1].v+=k; rest-=k; } } return flow-rest; } int main() { // freopen("1.in","r",stdin); n=read();m=read();k=read(); for(int i=1;i<=n+2;++i) f[i]=i; for(int i=1;i<=m;++i) { h[i]=read();r[i]=read(); for(int j=1;j<=r[i];++j) { c[i][j]=read(); if(c[i][j]==0) c[i][j]=n+1; else if(c[i][j]==-1) c[i][j]=n+2; } } for(int i=1;i<=m;++i) for(int j=1;j<=r[i];++j) { if(j==r[i]) merge(c[i][r[i]],c[i][1]); else merge(c[i][j],c[i][j+1]); } if(getf(n+1)!=getf(n+2)) {puts("0");return 0;} int maxflow=0,flow; int T=0; s=0;t=1;int cnt=2; for(int i=1;i<=n+2;++i) id[i][T]=++cnt; add(s,id[n+1][T],INF);add(id[n+2][T],t,INF); while(++T) { for(int i=1;i<=n+2;++i) id[i][T]=++cnt; for(int i=1;i<=n;++i) add(id[i][T-1],id[i][T],INF); add(s,id[n+1][T],INF);add(id[n+2][T],t,INF); for(int i=1;i<=m;++i) { int l=T%r[i]; if(!l) add(id[c[i][r[i]]][T-1],id[c[i][1]][T],h[i]); else add(id[c[i][l]][T-1],id[c[i][l+1]][T],h[i]); } while(bfs()) while(flow=dinic(s,INF)) maxflow+=flow; if(maxflow>=k) break; } printf("%d",T); return 0; }