Tehran的一家每天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)更多的出纳员在工作。
输入
输入文件的第一行为测试点个数(<= 20)。每组测试数据的第一行为24个整数表示R(0),R(1),..., R(23)(R(I)<= 1000)。接下来一行是N,表示申请者数目(0 <= N <= 1000),接下来每行包含一个整数tI (0 <= tI <= 23)。两组测试数据之间没有空行。
输出
对于每个测试点,输出只有一行,包含一个整数,表示需要出纳员的最少数目。如果无解,你应当输出“No Solution”。
样例输入
1
1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
5
0
23
22
1
10
样例输出
1
设num[i] 为来应聘的在第i个小时开始工作的人数
r[i] 为第i个小时至少需要的人数
x[i] 为招到的在第i个小时开始工作的人数
根据题意有:
0 <= x[i] <= num[i]
x[i] + x[i-1] + …+ x[i-7] >= r[i] (题目中的连续工作8小时)
再设 s[i] = x[1] + … + x[i]
则有: s[i] – s[i-1] >= 0
s[i-1] – s[i] >= –num[i]
s[i] – s[i-8] >= r[i], 8 <= i <= 24
s[i] – s[i+16] >= r[i] – s[24], 1<= i <= 7
还需要添加一个隐藏不等式: s[24] – s[0] >= ans(枚举的答案)
通过枚举s[24],来检测是否满足条件,题目是求最小值,即求最长路,以0为源点。
#include <stdio.h> #define maxN 25//最大顶点数,这里是s[i] #define inf 0x7fffffff struct Edge { int v, w, next; }edge[maxN * 30];//边 int edgeNum;//边总数 int dis[maxN];//最长路径中的dis[i]代表远点到i的最长距离 int r[maxN];//r[i]标示i时刻至少需要多少人 int num[maxN];//num[i]标示i时刻开始工作的人 bool vis[maxN];//标示i是否进入队列 int preEdge[maxN];//同一个起点的上一条边 int cnt[maxN];//入队列的次数 int queue[maxN * 30];//模拟队列 int n;//输入的n void addEdge(int u, int v, int w)//添加新边 { edge[edgeNum].v = v; edge[edgeNum].w = w; edge[edgeNum].next = preEdge[u];//以u为起点的上一条边 preEdge[u] = edgeNum ++; } int spfa(int ans)//spfa算法实现 { int head = 0, tail = 1; for (int i = 0; i <= 24; ++ i) { dis[i] = -inf; vis[i] = false; cnt[i] = 0; } queue[0] = 0; dis[0] = 0; while (head < tail) { int u = queue[head]; int p = preEdge[u]; vis[u] = true; while (p != -1) { int v = edge[p].v, w = edge[p].w; if (dis[v] < dis[u] + w) { dis[v] = dis[u] + w; if (!vis[v]) { vis[v] = true; queue[tail] = v; tail ++; } if(++cnt[v] > 24) { return 0; } } p = edge[p].next; } head ++; vis[u] = false; } if (dis[24] == ans) { return 1; } return 0; } void buildGraph(int ans)//建图这也是本题目的关键,建模。。。。 { edgeNum = 0; for (int i = 0; i <= 24; ++ i) { preEdge[i] = -1; } addEdge(0,24,ans); for(int i = 1;i <= 24;++i) { addEdge(i - 1,i,0); addEdge(i,i - 1,-num[i]); } for(int i = 1;i <= 8;++i) addEdge(i + 16,i,r[i] - ans); for(int i = 9;i <= 24;++i) addEdge(i - 8,i,r[i]); } int main()//主函数 { int cases; scanf("%d", &cases); while (cases --) { bool flag = false;; for (int i = 1; i <= 24; ++ i) { scanf("%d", &r[i]); num[i] = 0; } scanf("%d", &n); int t; for (int i = 0; i < n; ++ i) { scanf("%d", &t); num[t + 1] ++; } for (int i = 0; i <= n; ++ i) { buildGraph(i); if (spfa(i) == 1) { flag = true; printf("%d\n", i); break; } } if (!flag) { printf("No Solution\n"); } } return 0; }