[POJ 1149]PIGS
这个题是经典的网络流题目。在经过一次次的思想过滤后你会发现,这个问题的瓶颈(换猪)大可以理解为前面的顾客给后面的留一些不是?这样问题就简单多了。
按顺序整理出到访每个猪圈的顾客,对于到访每个猪圈的第一个顾客,从[tex]S[/tex]向其连一条容量为此猪圈中猪的数量的边。然后每个猪圈前面一个顾客要个给后面的留一些猪,这个可以直接连一条容量为无限大的边来表示。
最后每个顾客向[tex]T[/tex]连一条容量为其买猪数量上限的边,然后求一遍[tex]S[/tex]到[tex]T[/tex]的最大流,问题得解。
这个题还有一个优化就是,有一些边是可以合并的(比如说从[tex]S[/tex]流出的一些边是可能指向同一顾客的,同一对顾客之间的连边数可能不止1条),但是我没有做这个优化,照样A了……
代码:
#include <cstdio> #include <cstring> #include <vector> #include <queue> #include <algorithm> using std::vector; using std::queue; using std::min; const int maxn=105,maxm=1005; namespace Dinic{ struct Edge{ int u,v,cap,flow; }; vector<Edge> edges; vector<int> G[maxn]; int s,t; int m; inline void AddEdge(int u,int v,int cap){ edges.push_back((Edge){u,v,cap,0}); edges.push_back((Edge){v,u,0,0}); m=edges.size(); G[u].push_back(m-2); G[v].push_back(m-1); } bool vis[maxn]; int d[maxn]; inline bool bfs(){ register int i,u,siz; queue<int> Q; memset(vis,0,sizeof(vis)); d[s]=0; Q.push(s); vis[s]=true; while(!Q.empty()){ u=Q.front();Q.pop(); siz=G[u].size(); for(i=0;i<siz;i++){ Edge& e=edges[G[u][i]]; if(!vis[e.v] && e.cap>e.flow){ d[e.v]=d[u]+1; Q.push(e.v); vis[e.v]=true; } } } return vis[t]; } int cur[maxn]; int dfs(int x,int a){ if(x==t || a==0){ return a; } int f,flow=0,siz=G[x].size(); for(int& i=cur[x];i<siz;i++){ Edge& e=edges[G[x][i]]; if(d[e.v]==d[x]+1){ f=dfs(e.v,min(a,e.cap-e.flow)); if(f>0){ flow+=f; e.flow+=f; edges[G[x][i]^1].flow-=f; a-=f; if(a==0){ break; } } } } if(a>0){ d[x]=-1; } return flow; } inline int solve(int S,int T){ register int ans=0; s=S;t=T; while(bfs()){ memset(cur,0,sizeof(cur)); ans+=dfs(s,0x7fffffff); } return ans; } }; using Dinic::AddEdge; using Dinic::solve; int pigs[maxm]; vector<int> Use[maxm]; int main(){ register int i,j; int m,n; scanf("%d%d",&m,&n); for(i=1;i<=m;i++){ scanf("%d",&pigs[i]); } for(i=1;i<=n;i++){ int a,b,temp; scanf("%d",&a); for(j=1;j<=a;j++){ scanf("%d",&temp); Use[temp].push_back(i); } scanf("%d",&b); AddEdge(i,n+1,b); } for(i=1;i<=m;i++){ AddEdge(0,Use[i][0],pigs[i]); int siz=Use[i].size(); for(j=1;j<siz;j++){ AddEdge(Use[i][j-1],Use[i][j],0x7f7f7f7f); } } printf("%d\n",solve(0,n+1)); return 0; }
[COGS 727]太空飞行计划
第一次做最大权闭合图……so sad
关于最大权闭合图的做法,可以参考胡伯涛前辈的《最小割模型在信息学竞赛中的应用》。不过很麻烦的事是……打印方案。
注意,割走的边要么和[tex]S[/tex]相连,要么就和[tex]T[/tex]相连。如果一条从[tex]S[/tex]到[tex]E_i[/tex]被割走了,那么就说明[tex]E_i[/tex]没有被选择;如果一条从[tex]I_i[/tex]到[tex]T[/tex]的边被割走了,那么就说明[tex]I_i[/tex]被选择了。
于是乎,Dinic最后一次造层次图的时候(这次最终将不能到达[tex]T[/tex]),如果某个点(除了[tex]S[/tex]和[tex]T[/tex])被访问到了,那个点就被选择了。
最小割的结果是所有拒绝的实验的能赚的钱及所有选用的仪器消耗的钱的和。也就是说,答案就是[tex]p[/tex]的和减去最小割。
代码:
#include <cstdio> #include <iostream> #include <iterator> #include <cstring> #include <string> #include <sstream> #include <vector> #include <queue> #include <algorithm> #include <bitset> using namespace std; const int maxn=211; const int INF=0x7fffffff; #define REP(i,n) for(i=0;i<(n);i++) #define REP_B(i,n) for(i=1;i<=(n);i++) #define CL_ARR(x,v) memset(x,v,sizeof(x)) struct Edge{ int u,v,cap,flow; }; namespace Dinic{ vector<Edge> edges; vector<int> G[maxn]; int m; inline void AddEdge(int u,int v,int cap){ edges.push_back((Edge){u,v,cap,0}); edges.push_back((Edge){v,u,0,0}); m=edges.size(); G[u].push_back(m-2); G[v].push_back(m-1); } bool vis[maxn]; int d[maxn],cur[maxn]; int s,t; inline bool BFS(){ register int i,u; queue<int> Q; CL_ARR(vis,0); Q.push(s); d[s]=0; vis[s]=true; while(!Q.empty()){ u=Q.front(); Q.pop(); REP(i,G[u].size()){ Edge& e=edges[G[u][i]]; if(!vis[e.v] && e.cap>e.flow){ vis[e.v]=1; d[e.v]=d[u]+1; Q.push(e.v); } } } return vis[t]; } int DFS(int x,int a){ if(x==t || a==0) return a; int flow=0,temp; for(int& i=cur[x];i<G[x].size();i++){ Edge& e=edges[G[x][i]]; if(d[e.v]==d[x]+1){ temp=DFS(e.v,min(a,e.cap-e.flow)); if(temp>0){ e.flow+=temp; edges[G[x][i]^1].flow-=temp; flow+=temp; a-=temp; if(a==0) break; } } } return flow; } inline int Mincut(int S,int T){ s=S; t=T; register int ans=0; while(BFS()){ CL_ARR(cur,0); ans+=DFS(s,INF); } return ans; } }; vector<int> AnsList; int main(){ register int i,j,ans=0; int m,n,p,x; bool flag; string line; // ios::sync_with_stdio(false); // cin.tie(0); freopen("shuttle.in","r",stdin); freopen("shuttle.out","w+",stdout); ostream_iterator<int> output(cout," "); scanf("%d %d\n",&m,&n); REP_B(i,m){ getline(cin,line); stringstream ss(line); ss>>p; ans+=p; Dinic::AddEdge(0,i,p); while(ss>>x){ Dinic::AddEdge(i,m+x,INF); } } REP_B(i,n){ cin>>p; Dinic::AddEdge(i+m,n+m+1,p); } ans-=Dinic::Mincut(0,n+m+1); REP_B(i,m){ if(Dinic::vis[i]){ AnsList.push_back(i); } } copy(AnsList.begin(),AnsList.end(),output); cout<<endl; AnsList.clear(); REP_B(i,n){ if(Dinic::vis[i+m]){ AnsList.push_back(i); } } copy(AnsList.begin(),AnsList.end(),output); cout<<endl; cout<<ans<<endl; return 0; }
[POJ 2455]Secret Milking Machine
这个题要求最大边最小,很明显要二分答案。
考虑验证谓词[tex]C(x)[/tex]。我们可以将所有边权小于等于[tex]x[/tex]的边加入图,然后判断是否有[tex]T[/tex]条从1到N的不重复路径。
不过这个怎么做呢?我们可以把边加入(注意我们要加入的边都是无向边),并把容量设为1,从1到N跑一遍最大流,就是不重复路径条数。
为什么可以这样呢?每个边容量只有1,最多只能让一条路径使用并“推送”到终点,所以从1到N的最大流就是不重复路径条数辣。
代码:
#include <cstdio> #include <cstring> #include <vector> #include <queue> #include <algorithm> using namespace std; const int maxn=205,maxp=40005; #define REP(i,n) for(i=0;i<(n);i++) #define REP_B(i,n) for(i=1;i<=(n);i++) #define CL_ARR(x,v) memset(x,v,sizeof(x)) namespace Dinic{ struct Edge{ int u,v,cap,flow; }; vector<Edge> edges; vector<int> G[maxn]; int m; inline void AddEdge(int u,int v,int cap){ edges.push_back((Edge){u,v,cap,0}); edges.push_back((Edge){v,u,0,0}); m=edges.size(); G[u].push_back(m-2); G[v].push_back(m-1); } inline void ClearGraph(){ register int i; edges.clear(); m=0; REP(i,maxn){ G[i].clear(); } } bool vis[maxn]; int d[maxn],cur[maxn]; int s,t; inline bool BFS(){ register int i,u; queue<int> Q; CL_ARR(vis,0); Q.push(s); d[s]=0; vis[s]=true; while(!Q.empty()){ u=Q.front(); Q.pop(); REP(i,G[u].size()){ Edge& e=edges[G[u][i]]; if(!vis[e.v] && e.cap>e.flow){ vis[e.v]=1; d[e.v]=d[u]+1; Q.push(e.v); } } } return vis[t]; } int DFS(int x,int a){ if(x==t || a==0) return a; int flow=0,temp; for(int& i=cur[x];i<G[x].size();i++){ Edge& e=edges[G[x][i]]; if(d[e.v]==d[x]+1){ temp=DFS(e.v,min(a,e.cap-e.flow)); if(temp>0){ e.flow+=temp; edges[G[x][i]^1].flow-=temp; flow+=temp; a-=temp; if(a==0) break; } } } return flow; } inline int Maxflow(int S,int T){ s=S; t=T; register int ans=0; while(BFS()){ CL_ARR(cur,0); ans+=DFS(s,0x7f7f7f7f); } return ans; } }; struct Edge{ int u,v,d; bool operator <(const Edge& x) const{ return d<x.d; } }; Edge EdgePool[maxp]; int n,p,t; inline bool Check(int x){ register int i; Dinic::ClearGraph(); REP_B(i,p){ if(EdgePool[i].d>x) break; Dinic::AddEdge(EdgePool[i].u,EdgePool[i].v,1); Dinic::AddEdge(EdgePool[i].v,EdgePool[i].u,1); } if((Dinic::Maxflow(1,n))<t) return false; else return true; } int main(){ register int i,L=0,M,R=0; scanf("%d%d%d",&n,&p,&t); REP_B(i,p){ scanf("%d%d%d",&EdgePool[i].u,&EdgePool[i].v,&EdgePool[i].d); R=max(R,EdgePool[i].d+1); } sort(EdgePool+1,EdgePool+1+p); while(L<R){ M=L+(R-L)/2; if(Check(M)) R=M; else L=M+1; } printf("%d\n",L); return 0; }
[COGS 732]试题库
这个题和那个圆桌聚餐很像。
不过注意一点,一个题如果有多种类型,那么那个题最后只能归结到一种题型中。有些同学可能就因此怀疑样例(尽管有SPJ)。
所以最后的建图就是这样:从[tex]S[/tex]向所有题目结点连一条容量为1的边,每个题目向对应题型连一条容量为1的边,每个题型向[tex]T[/tex]连一条容量为该类型题目需要数量的边。跑一遍最大流即可,当且仅当最大流为[tex]m[/tex]时有解。
输出解吧其实也不难,直接判断从题目结点出发的弧是否满载即可。注意不要把反向弧和普通弧弄混。
代码:
#include <cstdio> #include <cstring> #include <vector> #include <queue> #include <algorithm> using namespace std; const int maxn=1030,maxk=25; #define REP(i,n) for(i=0;i<(n);i++) #define REP_B(i,n) for(i=1;i<=(n);i++) #define CL_ARR(x,v) memset(x,v,sizeof(x)) struct Edge{ int u,v,cap,flow; }; namespace Dinic{ vector<Edge> edges; vector<int> G[maxn]; int m; inline void AddEdge(int u,int v,int cap){ edges.push_back((Edge){u,v,cap,0}); edges.push_back((Edge){v,u,0,0}); m=edges.size(); G[u].push_back(m-2); G[v].push_back(m-1); } bool vis[maxn]; int d[maxn],cur[maxn]; int s,t; inline bool BFS(){ register int i,u; queue<int> Q; CL_ARR(vis,0); Q.push(s); d[s]=0; vis[s]=true; while(!Q.empty()){ u=Q.front(); Q.pop(); REP(i,G[u].size()){ Edge& e=edges[G[u][i]]; if(!vis[e.v] && e.cap>e.flow){ vis[e.v]=1; d[e.v]=d[u]+1; Q.push(e.v); } } } return vis[t]; } int DFS(int x,int a){ if(x==t || a==0) return a; int flow=0,temp; for(int& i=cur[x];i<G[x].size();i++){ Edge& e=edges[G[x][i]]; if(d[e.v]==d[x]+1){ temp=DFS(e.v,min(a,e.cap-e.flow)); if(temp>0){ e.flow+=temp; edges[G[x][i]^1].flow-=temp; flow+=temp; a-=temp; if(a==0) break; } } } return flow; } inline int Maxflow(int S,int T){ s=S; t=T; register int ans=0; while(BFS()){ CL_ARR(cur,0); ans+=DFS(s,0x7f7f7f7f); } return ans; } }; vector<int> ans[maxn]; int main(){ int k,n,u,p; register int i,j,m=0,flow; freopen("testlib.in","r",stdin); freopen("testlib.out","w+",stdout); scanf("%d%d",&k,&n); REP_B(i,k){ scanf("%d",&u); m+=u; Dinic::AddEdge(i+n,k+n+1,u); } REP_B(i,n){ scanf("%d",&p); Dinic::AddEdge(0,i,1); REP_B(j,p){ scanf("%d",&u); Dinic::AddEdge(i,n+u,1); } } flow=Dinic::Maxflow(0,n+k+1); if(flow!=m){ puts("NoSolution!"); return 0; } REP_B(i,n){ for(j=0;j<Dinic::G[i].size();j++){ Edge& e=Dinic::edges[Dinic::G[i][j]]; if(e.v>n && e.v<=(n+k) && e.cap==e.flow){ ans[e.v-n].push_back(i); } } } REP_B(i,k){ printf("%d:",i); REP(j,ans[i].size()){ printf(" %d",ans[i][j]); } putchar('\n'); } return 0; }
[BZOJ 1711]吃饭
这道题当初竟然没A……so sad
这题的建图要这么来:吃的、喝的、牛什么的统统拉进图里(不过牛本身只能同时享受一种食物,所以说点容量为1,要拆成俩点)。对于吃的,从[tex]S[/tex]向每个吃的连一条边。喝的每个向[tex]T[/tex]连一条边。牛本身按照喜好连即可(注意要拆成俩点!)。
代码:
/************************************************************** Problem: 1711 User: danihao123 Language: C++ Result: Accepted Time:20 ms Memory:864 kb ****************************************************************/ #include <cstdio> #include <cstring> #include <vector> #include <queue> using namespace std; const int maxn=405,INF=0x7f7f7f7f; namespace Dinic{ struct Edge{ int u,v,cap,flow; }; vector<Edge> edges; vector<int> G[maxn]; int m; inline void AddEdge(int u,int v,int cap){ edges.push_back((Edge){u,v,cap,0}); edges.push_back((Edge){v,u,0,0}); m=edges.size(); G[u].push_back(m-2); G[v].push_back(m-1); } bool vis[maxn]; int d[maxn]; int s,t; bool BFS(){ register int i,x; queue<int> Q; memset(vis,0,sizeof(vis)); d[s]=0; Q.push(s); vis[s]=true; while(!Q.empty()){ x=Q.front(); Q.pop(); for(i=0;i<G[x].size();i++){ Edge& e=edges[G[x][i]]; if(!vis[e.v] && e.cap>e.flow){ d[e.v]=d[x]+1; Q.push(e.v); vis[e.v]=true; } } } return vis[t]; } int cur[maxn]; int DFS(int x,int a){ if(x==t || a==0) return a; int flow=0,f; for(int& i=cur[x];i<G[x].size();i++){ Edge& e=edges[G[x][i]]; if(d[e.v]==d[x]+1){ f=DFS(e.v,min(a,e.cap-e.flow)); if(f>0){ flow+=f; e.flow+=f; edges[G[x][i]^1].flow-=f; a-=f; if(a==0) break; } } } return flow; } inline int Maxflow(int S,int T){ register int ans=0; s=S; t=T; while(BFS()){ memset(cur,0,sizeof(cur)); ans+=DFS(s,INF); } return ans; } }; int main(){ int N,F,D,f,d,x; register int i,j,t; scanf("%d%d%d",&N,&F,&D); t=2*N+F+D+1; for(i=1;i<=F;i++) Dinic::AddEdge(0,i,1); for(i=2*N+F+1;i<t;i++) Dinic::AddEdge(i,t,1); for(i=1;i<=N;i++) Dinic::AddEdge(F+2*i-1,F+2*i,1); for(i=1;i<=N;i++){ scanf("%d%d",&f,&d); for(j=1;j<=f;j++){ scanf("%d",&x); Dinic::AddEdge(x,2*i-1+F,1); } for(j=1;j<=d;j++){ scanf("%d",&x); Dinic::AddEdge(2*i+F,F+2*N+x,1); } } printf("%d\n",Dinic::Maxflow(0,t)); return 0; }
[BZOJ 1934]善意的投票/[BZOJ 2768]冠军调查
这个是一道题啊……不过都是挺经典的问题……
建图是这样的:支持0的从[tex]S[/tex]向此点连一条容量为1的边,支持1的就向[tex]T[/tex]连一条长度为1的边,朋友之间连一条长度为1的边。跑一遍最小割即可。
这个的解释?最优情况下任何人违背自己的意见都是因为怕和朋友冲突,和朋友冲突则是因为没有违背自己的意见(废话)。所以拆了最小割就解决掉了所有冲突问题。
代码:
/************************************************************** Problem: 2768 User: danihao123 Language: C++ Result: Accepted Time:60 ms Memory:7668 kb ****************************************************************/ #include <cstdio> #include <cstring> #include <queue> #include <vector> #include <algorithm> using namespace std; const int maxn=305; namespace Dinic{ struct Edge{ int u,v,cap,flow; }; vector<Edge> edges; vector<int> G[maxn]; int s,t; int m; inline void AddEdge(int u,int v,int cap){ edges.push_back((Edge){u,v,cap,0}); edges.push_back((Edge){v,u,0,0}); m=edges.size(); G[u].push_back(m-2); G[v].push_back(m-1); } bool vis[maxn]; int cur[maxn],d[maxn]; bool bfs(){ register int i,u; queue<int> Q; memset(vis,0,sizeof(vis)); Q.push(s); d[s]=0; vis[s]=true; while(!Q.empty()){ u=Q.front(); Q.pop(); for(i=0;i<G[u].size();i++){ Edge& e=edges[G[u][i]]; if(!vis[e.v] && e.cap>e.flow){ d[e.v]=d[u]+1; Q.push(e.v); vis[e.v]=true; } } } return vis[t]; } int dfs(int x,int a){ if(x==t || a==0) return a; int f,flow=0; for(int& i=cur[x];i<G[x].size();i++){ Edge& e=edges[G[x][i]]; if((d[x]+1==d[e.v]) && ((f=dfs(e.v,min(a,e.cap-e.flow)))>0)){ flow+=f; e.flow+=f; edges[G[x][i]^1].flow-=f; a-=f; if(a==0) break; } } return flow; } int MinCut(int S,int T){ register int ans=0; s=S; t=T; while(bfs()){ memset(cur,0,sizeof(cur)); ans+=dfs(s,0x7fffffff); } return ans; } }; int main(){ int n,m; int u,v; register int i; scanf("%d%d",&n,&m); for(i=1;i<=n;i++){ scanf("%d",&u); if(!u){ Dinic::AddEdge(0,i,1); }else{ Dinic::AddEdge(i,n+1,1); } } for(i=1;i<=m;i++){ scanf("%d%d",&u,&v); Dinic::AddEdge(u,v,1); Dinic::AddEdge(v,u,1); } printf("%d\n",Dinic::MinCut(0,n+1)); return 0; }
[COGS 729]圆桌聚餐
第一次做这种”大型“网络流题(抱歉我是炒鸡大局若)。
注意同一个单位的人不可同桌聚餐,说明同一单位的人最多只能往某一桌子派一人!然后建图就很简单了:源点往每个单位连流量为[tex]r_i[/tex]的边,每个单位分别向每一个桌连一条流量为1的边,每个桌向汇点连一条流量为[tex]c_i[/tex]的边。跑一遍网络流即可。
这题坑点在于要输出解。输出解的时候注意不要把反向弧和正常的弧搞混。
代码:
#include <cstdio> #include <cstring> #include <vector> #include <queue> #include <algorithm> using namespace std; const int maxn=425; #define REP(i,n) for(i=0;i<(n);i++) #define REP_B(i,n) for(i=1;i<=(n);i++) #define CL_ARR(x,v) memset(x,v,sizeof(x)) struct Edge{ int u,v,cap,flow; }; namespace Dinic{ vector<Edge> edges; vector<int> G[maxn]; int m; inline void AddEdge(int u,int v,int cap){ edges.push_back((Edge){u,v,cap,0}); edges.push_back((Edge){v,u,0,0}); m=edges.size(); G[u].push_back(m-2); G[v].push_back(m-1); } bool vis[maxn]; int d[maxn],cur[maxn]; int s,t; inline bool BFS(){ register int i,u; queue<int> Q; CL_ARR(vis,0); Q.push(s); d[s]=0; vis[s]=true; while(!Q.empty()){ u=Q.front(); Q.pop(); REP(i,G[u].size()){ Edge& e=edges[G[u][i]]; if(!vis[e.v] && e.cap>e.flow){ vis[e.v]=1; d[e.v]=d[u]+1; Q.push(e.v); } } } return vis[t]; } int DFS(int x,int a){ if(x==t || a==0) return a; int flow=0,temp; for(int& i=cur[x];i<G[x].size();i++){ Edge& e=edges[G[x][i]]; if(d[e.v]==d[x]+1){ temp=DFS(e.v,min(a,e.cap-e.flow)); if(temp>0){ e.flow+=temp; edges[G[x][i]^1].flow-=temp; flow+=temp; a-=temp; if(a==0) break; } } } return flow; } inline int Maxflow(int S,int T){ s=S; t=T; register int ans=0; while(BFS()){ CL_ARR(cur,0); ans+=DFS(s,0x7f7f7f7f); } return ans; } }; int main(){ int n,m,temp; bool flag; register int i,j,tag,end,Expect=0; freopen("roundtable.in","r",stdin); freopen("roundtable.out","w+",stdout); scanf("%d%d",&n,&m); end=n+m+1; REP_B(i,n){ scanf("%d",&temp); Expect+=temp; Dinic::AddEdge(0,i,temp); } REP_B(i,m){ scanf("%d",&temp); tag=i+n; Dinic::AddEdge(tag,end,temp); REP_B(j,n){ Dinic::AddEdge(j,tag,1); } } temp=Dinic::Maxflow(0,end); if(temp<Expect){ puts("0"); return 0; } puts("1"); REP_B(i,n){ vector<int> V; REP(j,Dinic::G[i].size()){ Edge& e=Dinic::edges[Dinic::G[i][j]]; if(e.v>n && e.v<=m+n && e.cap==e.flow) V.push_back(e.v); } sort(V.begin(),V.end()); flag=false; REP(j,V.size()){ if(flag) putchar(' '); flag=true; printf("%d",V[j]-n); } putchar('\n'); } return 0; }