原文链接http://www.cnblogs.com/zhouzhendong/p/8326021.html
题目传送门 - POJ1459
题意概括
多组数据。
对于每一组数据,首先一个数n,表示有n个保安(n=0时输入结束)。
接下来分别描述n个保安的信息。
对于每一个保安,首先两个整数K,M,分别表示他的空余时间段数和他一天中的最多工作时间。
接下来K行,每行h1:m1 h2:m2格式输入两个时间点,表示他从h1:m1时刻到h2:m2时刻是空余的。
现在我们要安排保安。
保安的开始工作和结束工作时间只可以安排在整点或者半点,即m=0或m=30。
现在对于每一组数据,我们问一天中保安数量最少的时刻最多有多少个保安。
题解
网络流学习资源链接 -> 传送门
假如我们已经知道了答案,答案为ans。
那么我们可以考虑判断这个答案的正确性。
我们搞一个超级源点S和一个超级汇点T,然后把所有的保安当作节点,所有的时刻当作节点。
那么我们构图方法就很简单了。
由于保安的开始工作和结束工作时间只可以安排在整点或者半点,所以我们划分一下时刻,把所有的时间段划分成48个。
- 对于每一个保安Pi,我们建立一条S->Pi的边,容量为Mi div 30
- 对于每一个保安Pi,如果他在第Tj个时间段是空闲的,那么我们建立一条Pi->Tj的边,容量为1
- 对于每一个时间点Ti,我们建立一条Ti->T的边,容量为ans
如此建边,我们就只需要跑一跑最大流,然后看最大流是不是等于ans*48就可以了。
于是我们推广出两种算法:
二分答案 VS 逐个添加
本人更倾向于逐个添加,因为网络流的特殊性质。
代码
#include <cstring> #include <cstdio> #include <algorithm> #include <cstdlib> #include <cmath> using namespace std; const int N=48+50+5,M=(50*48*2+48+50)*2+233; bool isd(char ch){ return ‘0‘<=ch&&ch<=‘9‘; } int read(){ int res=0,f=1; char ch=getchar(); while (!isd(ch)&&ch!=‘-‘) ch=getchar(); if (ch==‘-‘) f=-1,ch=getchar(); while (isd(ch)) res=(res<<3)+(res<<1)+ch-48,ch=getchar(); return res*f; } int n,Free[1440]; struct edge{ int x,y,cap,flow,nxt; }; struct gragh{ int cnt,fst[N],dist[N],n,S,T,cur[N],num[N],p[N]; int q[N],head,tail; edge e[M]; void set(int _S,int _T,int _n){ S=_S,T=_T,n=_n,cnt=1; memset(fst,0,sizeof fst); } void add(int a,int b,int c){ cnt++; e[cnt].x=a,e[cnt].y=b,e[cnt].cap=c,e[cnt].flow=0; e[cnt].nxt=fst[a],fst[a]=cnt; cnt++; e[cnt].x=b,e[cnt].y=a,e[cnt].cap=0,e[cnt].flow=0; e[cnt].nxt=fst[b],fst[b]=cnt; } void bfs(){ memset(dist,-1,sizeof dist); head=tail=dist[T]=0,q[++tail]=T; while (head<tail) for (int x=q[++head],y,i=fst[x];i;i=e[i].nxt) if (e[i].cap==0&&dist[y=e[i].y]==-1) dist[q[++tail]=y]=dist[x]+1; for (int i=1;i<=n;i++) if (dist[i]==-1) dist[i]=n; } void init(){ bfs(); memset(num,0,sizeof num); for (int i=1;i<=n;i++) num[dist[i]]++,cur[i]=fst[i]; } int Augment(int &x){ int ex_flow=1e9; for (int i=T;i!=S;i=e[p[i]].x) if (e[p[i]].cap-e[p[i]].flow<=ex_flow) ex_flow=e[p[i]].cap-e[p[i]].flow,x=e[p[i]].x; for (int i=T;i!=S;i=e[p[i]].x) e[p[i]].flow+=ex_flow,e[p[i]^1].flow-=ex_flow; return ex_flow; } int ISAP(){ int x=S,y,MaxFlow=0; init(); while (dist[S]<n){ if (x==T){ MaxFlow+=Augment(x); continue; } bool found=0; for (int i=cur[x];i;i=e[i].nxt){ y=e[i].y; if (dist[y]+1==dist[x]&&e[i].cap>e[i].flow){ p[y]=cur[x]=i,x=y,found=1; break; } } if (!found){ int d=n+1; for (int i=fst[x];i;i=e[i].nxt) if (e[i].cap>e[i].flow) d=min(d,dist[e[i].y]+1); if (!--num[dist[x]]) return MaxFlow; num[dist[x]=d]++,cur[x]=fst[x],x=x==S?x:e[p[x]].x; } } return MaxFlow; } }g; bool check(int st,int en){ for (int i=st;i<en;i++) if (!Free[i]) return 0; return 1; } void solve(){ int S=1,T=2; g.set(S,T,n+48+2); for (int i=1;i<=n;i++){ int a=read(),b=read(); memset(Free,0,sizeof Free); g.add(S,i+2,b/30); for (int j=1;j<=a;j++){ int h1=read(),m1=read(),h2=read(),m2=read(); int v1=h1*60+m1,v2=h2*60+m2; if (v1<v2) for (int k=v1;k<v2;k++) Free[k]=1; else { for (int k=v1;k<1440;k++) Free[k]=1; for (int k=0;k<v2;k++) Free[k]=1; } } for (int j=1;j<=48;j++) if (check((j-1)*30,j*30)) g.add(i+2,n+j+2,1); } int ans=0; for (int i=1;i<=48;i++) g.add(n+i+2,T,1); while (g.ISAP()==48){ ans++; for (int i=1;i<=48;i++) g.add(n+i+2,T,1); } printf("%d\n",ans); } int main(){ while (n=read()) solve(); return 0; }