想看更多的解题报告: http://blog.csdn.net/wangjian8006/article/details/7870410
转载请注明出处:http://blog.csdn.net/wangjian8006
题目大意:
德黑兰的一家每天24小时营业的超市,需要一批出纳员来满足它的需求。超市经理雇佣你来帮他解决一个问题————超市在每天的不同时段需要不同数目的出纳员(例如,午夜只需一小批,而下午则需要很多)来为顾客提供优质服务,他希望雇佣最少数目的纳员。
超市经历已经提供一天里每一小时需要出纳员的最少数量————R(0),R(1),...,R(23)。R(0)表示从午夜到凌晨1:00所需要出纳员的最少数目;R(1)表示凌晨1:00到2:00之间需要的;等等。每一天,这些数据都是相同的。有N人申请这项工作,每个申请者i在每天24小时当中,从一个特定的时刻开始连续工作恰好8小时。定义ti(0<=ti<=23)为上面提到的开始时刻,也就是说,如果第i个申请者被录用,他(或她)将从ti时刻开始连续工作8小时。
试着编写一个程序,输入R(i),i=0,...,23,以及ti,i=1,...,N,它们都是非负整数,计算为满足上述限制需要雇佣的最少出纳员数目、在每一时刻可以有比对应R(i)更多的出纳员在工作
输入描述:
输入文件的第1行为一个整数T,表示输入文件中测试数据的数目(至多20个)。每个测试数据第一行为24个整数,表示R(0),R(1),...,R(23),R(i)最大可以取到1000。接下来一行是一个整数N,表示申请者的数目,0<=N<=1000。接下来有N行,每行为一个整数ti,0<=ti<=23,测试数据之间没有空行。
输出描述:
对输入文件中的每个测试数据,输出占一行,为需要雇佣的出纳员的最少数目。如果某个测试数据没有解。则输出"No Solution"。
注:题目描述来自于"图论算法理论、实现及应用"。
解题思路:
这题太难了,想了半天没有一点思路,剩下半天找资料分析,下面是黑书————《算法艺术与信息学竞赛》
中的描述:
还有一些:
差分约束的关键是 要充分利用所给条件
建立数组的 逻辑意义 如此题的dis[]数组(s[]数组) 建立差分约束系统即(找出差分约束关系),
摘自冯威论文——《数与图的完美结合》
设num[ i ]为i时刻能够开始工作的人数,x[ i ]为 第 i 时刻实际雇佣的人数,那么x[ I ]<=num[ I ]。
设r[ i ](输入的每一时刻所需的人数)为i时刻至少需要工作的人数,于是有如下关系:
x[ I-7 ]+x[ I-6 ]+x[ I-5 ]+x[ I-4 ]+x[ I-3 ]+x[ I-2 ]+x[ I-1 ]+x[ I ]>=r[ I ]
设s[ I ]=x[ 1 ]+x[ 2 ]…+x[ I ],得到
0<=s[ I ]-s[ I-1 ]<=num[ I ], 0<=I<=23
s[ I ]-s[ I-8 ]>=r[ I ], 8<=I<=23 建立关系
s[ 23 ]+s[ I ]-s[ I+16 ]>=r[ I ], 0<=I<=7 建立可以循环的关系
对于以上的几组不等式,我们采用一种非常笨拙的办法处理这一系列的不等式(其实也是让零乱的式子变得更加整齐,易于处理)。首先我们要明白差分约束系统的应用对象(它通常针对多个二项相减的不等式的)于是我们将上面的所有式子都转化成两项未知项在左边,另外的常数项在右边,且中间用>=连接的式子,即:
s[ I ]-s[ I-1 ]>=0 (0<=I<=23)
s[ I-1 ]-s[ I ]>=-num[ I ] (0<=I<=23)
s[ I ]-s[ I-8 ]>=r[ I ] (8<=I<=23)
s[ I ]-s[ I+16 ]>=r[ I ]-s[ 23 ] (0<=I<= 7)
论文有点小小的漏洞,也导致了它论文附带的程序是错误的,有BUG
可行方案中还差一个约束条件
S[23] - S[-1] >= sum;sum为雇佣的出纳员总数
将所有形如 A-B>=C 的式子 我们从节点B 引出一条有向边指向A边的权值为C (这里注意由于左右确定,式子又是统一的>=的不等式,所以A和B是相对确定的,边是一定是指向A的) ,图就建成了 。
在程序中,我用0作为上面说的-1来构图
注:来自于http://www.cnblogs.com/acSzz/archive/2012/04/29/2476073.html
根据这些描述终于把代码调试出来了。...
#include <iostream>
using namespace std;
#define MAXT 26
#define MAXE 1000
#define INF INT_MAX
typedef struct{
int s,t,w;
}Edge;
Edge edge[MAXE];
int r[MAXT]; //某个小时需要多少人
int t[MAXT]; //在第i个小时应聘的人数
int d[MAXT];
int n,edgesum,ans;
void add(int s,int t,int w){
edge[edgesum].s=s;
edge[edgesum].t=t;
edge[edgesum++].w=w;
}
void buildgragh(int x){
int i;
edgesum=48;
for(i=1;i<=16;i++) add(i+8,i,-r[i+8]);
for(i=17;i<=24;i++) add(i-16,i,x-r[i-16]);
add(24,0,-x);
}
int bellman_ford(){
int i,j;
for(i=0;i<=24;i++) d[i]=INF;
d[0]=0;
for(i=0;i<=24;i++){
for(j=0;j<edgesum;j++){
if(d[edge[j].s]+edge[j].w<d[edge[j].t]){
d[edge[j].t]=d[edge[j].s]+edge[j].w;
}
}
}
for(j=0;j<edgesum;j++){ //判断是否有负环
if(d[edge[j].s]+edge[j].w<d[edge[j].t]){
return 0;
}
}
return 1;
}
void Besearch(int low,int high){
if(low>high) return;
int mid=(low+high)/2;
buildgragh(mid);
if(bellman_ford()){
ans=mid;
Besearch(low,mid-1);
}else{
Besearch(mid+1,high);
}
}
int main(){
int i,Case,x;
scanf("%d",&Case);
while(Case--){
memset(t,0,sizeof(t));
for(i=1;i<=24;i++) scanf("%d",&r[i]);
scanf("%d",&n);
for(i=1;i<=n;i++){ //x+1时刻的应聘人数加1
scanf("%d",&x);
t[x+1]++;
}
edgesum=0;
for(i=1;i<=24;i++){ //这48条边是固定的,每个图都需要
add(i-1,i,t[i]);
add(i,i-1,0);
}
ans=-1; //对人数二分枚举求解
Besearch(0,n);
if(ans==-1) printf("No Solution\n");
else printf("%d\n",ans);
}
return 0;
}