ZOJ3229:Shoot the Bullet——题解
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ZOJ3229:Shoot the Bullet——题解相关的知识,希望对你有一定的参考价值。
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3229
题目大意:射命丸文要给幻想乡的居民照相,共照n天m个人,每天射命丸文照相数不多于d个,且一个人n天一共被拍的照片不能少于g个,且每天可照的人有限制,且这些人今天照的相片必须在[l,r]以内,求是否有可行解,如果有则输出最多照片数,并且输出每天每个可以被照的人的被照的照片数。
————————————————————————
https://www.cnblogs.com/kane0526/archive/2013/04/05/3001108.html
按照这篇博客的顺序先刷了这道题。
(话说射命丸文怎么就成屌丝了……)
建图很显然,源点到每天连一条[0,d],每天到每天能连的人建[l,r],每个人到汇点连[g,INF]。
现在就是有源汇上下界网络流的题了。
按照无源汇上下界网络流的想法(可参考我的上一篇博客ZOJ2314)是很容易建的,有源汇的话我们就手动把它变成无源汇的即可。
那么我们从汇点向源点连一条[0,INF]不就可以了吗?
接下来就是我们建立一个超级源汇点,跑一遍无源汇上下界网络流即可。
但!是!还!没!完!我们跑完的流量实际上只是流向超级汇点的流量,它只是流向汇点的流量的一部分,在残余网络里还可能存在一些被卡在管子里的流量无法流向超级汇点但可以流向汇点。
所以我们机智的删掉了超级源汇点,再跑一遍从源点到汇点的最大流即可。
至于每个人每天的照片,那自然是每天到每个人的反边的容量+下界了!
!注意!18.1.6更新,修正了代码的问题和表述的问题:我们实际上对于卡在管子里的流不仅仅是原图,还有些我们做上下界网络流的边,所以这些边我们不能删。也就是说,总流量就等于我们删除超级源汇点之后的最大流,因为我们把流向超级汇点的流量也一并流向了汇点。
#include<cstdio> #include<iostream> #include<cmath> #include<cstring> #include<algorithm> using namespace std; const int N=1501; const int M=740001; const int INF=1e9; inline int read(){ int X=0,w=0;char ch=0; while(!isdigit(ch)){w|=ch==\'-\';ch=getchar();} while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); return w?-X:X; } struct node{ int nxt; int to; int w; }edge[M]; int head[N],du[N],id[366][1001],low[366][1001],cnt=-1; int S,T; void add(int u,int v,int w){ cnt++; edge[cnt].to=v; edge[cnt].w=w; edge[cnt].nxt=head[u]; head[u]=cnt; } int lev[N],cur[N],dui[N]; bool bfs(int m){ int r=0; for(int i=1;i<=m;i++){ lev[i]=-1; cur[i]=head[i]; } dui[0]=S,lev[S]=0; int u,v; for(int l=0;l<=r;l++){ u=dui[l]; for(int e=head[u];e!=-1;e=edge[e].nxt){ v=edge[e].to; if(edge[e].w>0&&lev[v]==-1){ lev[v]=lev[u]+1; r++; dui[r]=v; if(v==T)return 1; } } } return 0; } int dinic(int u,int flow,int m){ if(u==m)return flow; int res=0,delta; for(int &e=cur[u];e!=-1;e=edge[e].nxt){ int v=edge[e].to; if(edge[e].w>0&&lev[u]<lev[v]){ delta=dinic(v,min(edge[e].w,flow-res),m); if(delta>0){ edge[e].w-=delta; edge[e^1].w+=delta; res+=delta; if(res==flow)break; } } } if(res!=flow)lev[u]=-1; return res; } inline void init(){ memset(head,-1,sizeof(head)); memset(du,0,sizeof(du)); memset(id,0,sizeof(id)); cnt=-1; return; } int main(){ int n,m; while(scanf("%d%d",&n,&m)!=EOF&&n){ int st=m+n+1,ed=m+n+2; init(); for(int i=1;i<=m;i++){ int g=read(); add(i,ed,INF-g); add(ed,i,0); du[i]-=g; du[ed]+=g; } for(int i=m+1;i<=m+n;i++){ int c=read(),d=read(); add(st,i,d); add(i,st,0); for(int j=1;j<=c;j++){ int t=read()+1,l=read(),r=read(); add(i,t,r-l); add(t,i,0); du[i]-=l; du[t]+=l; id[i-m][t]=cnt; low[i-m][t]=l; } } add(ed,st,INF);add(st,ed,0); S=ed+1;T=S+1; int ans=0,full=0; for(int i=1;i<=m+n+2;i++){ if(du[i]>0){ add(S,i,du[i]); add(i,S,0); full+=du[i]; }else if(du[i]<0){ add(i,T,-du[i]); add(T,i,0); } } while(bfs(T)==1)ans+=dinic(S,INF,T); if(ans!=full)puts("-1"); else{ head[S]=head[T]=-1; S=st;T=ed;ans=0; while(bfs(ed)==1)ans+=dinic(S,INF,T); printf("%d\\n",ans); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(id[i][j]){ printf("%d\\n",edge[id[i][j]].w+low[i][j]); } } } } puts(""); } return 0; }
以上是关于ZOJ3229:Shoot the Bullet——题解的主要内容,如果未能解决你的问题,请参考以下文章
zoj 3229 Shoot the Bullet(有源汇上下界最大流)
ZOJ Problem Set - 3229 Shoot the Bullet 有上下界网络流+流量输出
[zoj] 3229 Shoot the Bullet || 有源汇上下界最大流