【题目大意】: 给出一个超市24小时各需要R[i]个雇员工作,有N个雇员可以雇佣,他们开始工作时间分别为A[i],求需要的最少的雇员人数。
这题目在黑书上有详细的解析,题目的关键是在构图上,把约束条件找出来,再按照条件加边就OK了,不过,我是参考课件里面才过的
【算法分析】: 用差分约束,难点是建立模型。设X[i]为0-i小时内工作的人数(X[24]即为所求),R[i]为第(i-1)-i小时时需要在工作的人数,A[i]可以在第i-1小时开始工作。可以建立起以下约束不等式:
0 <= X[i]-X[i-1] <= A[i]; 1 <= I <= 24;
X[i] – X[i-8] >= R[i]; 8 <= I <= 24;
X[24] – (X[16+i]-X[i]) >= R[i]; 1 <= I < 8;
统一化为<=不等式,则可化为求X[0]-X[24] <= --ans; 即求24到0的最短距离。如果化为>=不等式,则比较直观,X[24]- X[0] >= ans; 求0到24的最长距离。如果有圈出现则无解。二分答案找到最小的ans,使得约束系统有解。特别注意一点,X[24]-X[0] >= ans也是要作为一个约束不等式,添加(0, 24, ans)的边。
#include<iostream> #include<string> #define MAXINT 9999999 #define MAXN 30 using namespace std; int dis[MAXN],num; int n,m,r[25],t[25]; //r[],(i-1)-i小时内需要工作人数. //t[],第(i-1)小时可以开始工作的人数. struct edge { int u,w,v; }e[MAXN*5]; void add(int u,int v,int w) { e[num].u=u; e[num].w=w; e[num].v=v; num++; } bool bellman_ford() { for(int i=0;i<=24;i++) dis[i]=-MAXINT; dis[24]=0; bool rel=false; for(int i=1;i<=24;i++) { for(int j=0;j<num;j++) { int x=e[j].v; if(dis[x]>dis[e[j].u]+e[j].w) { dis[x]=dis[e[j].u]+e[j].w; rel=true; } } if(!rel) //没有可松弛操作,则一定有解了 return true; } for(int j=0;j<num;j++) { int x=e[j].v; if(dis[x]>dis[e[j].u]+e[j].w) return false; } return true; } int main() { int n1,cas,a,minn,maxn; cin>>cas; while(cas--) { minn=-1; memset(t,0,sizeof(t)); for(int i=1;i<=24;i++) { cin>>r[i]; if(r[i]>minn) minn=r[i]; } cin>>n1; maxn=n1; //因为最少要有max(r[i])个人,最多有n1个人。 for(int i=0;i<n1;i++) { cin>>a; t[++a]++; } num=0; for(int i=1;i<=24;i++) { add(i-1,i,t[i]); add(i,i-1,0); } for(int i=8;i<=24;i++) add(i,i-8,-r[i]); int edge_num=num;//edge_num保存已经加好的边数 int res=-1; int left=minn,right=maxn,mid; while(left<=right)//二分枚举答案 { num=edge_num; mid=(right+left)/2; for(int i=1;i<8;i++) add(i,i+16,-r[i]+mid); add(24,0,-mid); //这里其实有个隐藏的约束条件X[24]-X[0]>=mid //转换为X[0]-X[24] <= -mid; if(bellman_ford())//如果有解,那么再找更小的解 { right=mid-1;res=mid; } else left=mid+1; } if(res==-1) cout<<"No Solution"<<endl; else cout<<res<<endl; } return 0; }