由于数据量小,用Bellman_Ford就能过,而且不用要源点。
这道差分约束题困了我好久,WA了好多次,主要是不理解题意,在那乱写,后来WA之后,看别人解题报告,又是狂改,最后还是没能过!今天早上,在真正理解之后,把需要修改地方给删了,又重写了一下,终于找到错误了,原来边数赋值错了。discuss里说的n = 4的情况是误导大家的,其实是少写了个条件,题目和数据都是没问题的!
这题《算法艺术与信息学竞赛》中有解答,主要有四种情况:
这题我就是用二分做的,对sum能取到的值二分,在这里有一个点s[-1],我是将所有点都往后移一位来处理的,就空出一个0点可以代替这里的-1点,n = 4的情况就是这里的第三种情况没有考虑造成的。
多于第四个式子可以这样解释:第i个小时雇佣的出纳员数 = 8小时以前雇佣的出纳员数+第i个小时需要的出纳员数(因为这第i个小时的出纳员可以是前8个小时中任意一个小时雇佣的,因为他会一直工作8个小时,也就覆盖到第i个小时了^-^);
代码
#include<stdio.h>
#define INF 0xfffffff
#define NN 26
#define MM 100
int edNum, N, sum;
struct node{
int s, e, v;
}edge[MM];
int dis[NN];
int r[NN];
int t[NN];
void add(int a, int b, int c){
edge[edNum].s = a;
edge[edNum].e = b;
edge[edNum].v = c;
edNum++;
}
void change(int ans){
int i, sta, end;
// 开始就这个地方错了,当成24赋值了
edNum = 48;
for (i = 1; i <= 24; i++){
// 这个地方的处理也成为这一题的关键
if (i > 8){
add(i, i - 8, -r[i]);
}else{
add(i, i + 16, ans - r[i]);
}
}
// 这里就是为什么n = 4 很多人过不了
add(24, 0, -ans);
}
int Bellman_Ford(){
int i, j;
for (i = 0; i <= 24; i++) dis[i] = INF;
for (i = 0; i <= 24; i++){
for (j = 0; j < edNum; j++){
if (dis[edge[j].e] > dis[edge[j].s] + edge[j].v){
dis[edge[j].e] = dis[edge[j].s] + edge[j].v;
}
}
}
for (j = 0; j < edNum; j++){
if (dis[edge[j].e] > dis[edge[j].s] + edge[j].v){
return 0;
}
}
return 1;
}
/*这里用的二分来查找最小的ans*/
void solve(int sum)
{
int low = 0;
int hig = sum;
int ans = -1;
do{
int mid = (low + hig) >> 1;
change(mid);
if (Bellman_Ford()){
ans = mid;
hig = mid - 1;
}
else low = mid + 1;
}while (low <= hig);
if (ans == -1) puts("No Solution");
else printf("%d\n", ans);
}
int main()
{
int T, i, R, k;
scanf("%d", &T);
while (T--){
sum = 0;
edNum = 0;
for (i = 1; i <= 24; i++){
scanf("%d", &r[i]);
sum += r[i];
t[i] = 0;
}
scanf("%d", &N);
if (N < sum){
sum = N;
}
for (i = 0; i < N; i++){
scanf("%d", &k);
t[k + 1]++;
}
for (i = 1; i <= 24; i++){
add(i - 1, i, t[i]);
add(i, i - 1, 0);
}
solve(sum);
}
return 0;
}