题目:http://acm.hdu.edu.cn/showproblem.php?pid=1534
刚开始以为是AOE网络,将每个活动分解成开始点和结束点,感觉只要找到每个任务的最早结束时间,减去活动耗时就是可行的开始时间了,构图之后理论上所有点应该满足一个时间先后的顺序,拓扑排序的过程就行了,没想到WA了,大为不解,为什么不可以。。。
好吧,以差分约束的方式分析(差分约束之后解是唯一的,为什么题目还说是special judge呢):
先回忆一下,差分约束系统是用最短路模型解决的一类问题,针对形如x - y <= c的不等式组,物理含义上可以理解为,从起点到a点的最短路长度x <= 从起点到b点的最短路长度y + 常数c,所以构图时从b点向a点引一条长度为c的有向边。
对于本题,令每个活动的耗时为cost[i]、最早开始时间为dis[i](开始干活之前时间是0),则四种关系分别是:
(1)SAS x y:即x在y开始之后开始,则dis[x] >= dis[y],即dis[y] - dis[x] <= 0 <=> x到y有一条长度为0的有向边;
(2)SAF x y:即x在y完成之后开始,则dis[x] >= dis[y] + cost[y],即dis[y] - dis[x] <= -cost[y] <=> x到y有一条长度为-cost[y]的有向边;
(3)FAS x y:即x在y开始之后完成,则dis[x] + cost[x] >= dis[y],即dis[y] - dis[x] <= cost[x] <=> x到y有一条长度为cost[x]的有向边;
(4)FAF x y:即x在y完成之后完成,则dis[x] + cost[x] >= dis[y] + cost[y],即dis[y] - dis[x] <= cost[x] - cost[y] <=> x到y有一条长度为cost[x]-cost[y]的有向边。
这样构图之后还要加一个超级源点作为起点,另外需要注意的是,最早开始的活动应该输出开始时间为0,因此我们在求出dis[]之后,要将所有的dis[i]减去min{dis[]}进行输出。
#include <cstdio>
#include <cstring>
#include <utility>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
#define MAX 5005
typedef pair<int,int> PII;
int N, cost[MAX], dis[MAX], inq[MAX];
vector<PII> vertex[MAX];
bool vis[MAX];
queue<int> Q;
bool spfa()
{
while(!Q.empty()) Q.pop();
memset(vis + 1, 0x00, N);
memset(dis + 1, 0x7F, N << 2);
memset(inq + 1, 0x00, N << 2);
inq[0] = dis[0] = 0;
Q.push(0); vis[0] = true;
while(!Q.empty()){
int x = Q.front(); Q.pop(); vis[x] = false;
vector<PII>& v = vertex[x];
for(int i = v.size() - 1; i > -1; --i){
int y = v[i].first, d = v[i].second;
if(dis[y] > dis[x] + d){
dis[y] = dis[x] + d;
if(!vis[y]){
Q.push(y);
vis[y] = true;
if(++inq[y] > N) return false;
}
}
}
}
return true;
}
int main()
{
int kase = 0, i, x, y, d;
char s[4];
while(++kase, scanf("%d", &N), N){
//init
for(i = 0; i <= N; ++i) vertex[i].clear();
//input
for(i = 1; i <= N; ++i) scanf("%d", cost + i);
while(scanf("%s", s), s[0] != '#'){
scanf("%d%d", &x, &y);
if(s[0] == 'S'){
if(s[2] == 'S') d = 0;
else d = -cost[y];
}
else{
if(s[2] == 'S') d = cost[x];
else d = cost[x] - cost[y];
}
vertex[x].push_back(PII(y, d));
}
//add super source
for(i = 1; i <= N; ++i) vertex[0].push_back(PII(i, 0));
//figure out dis
if(spfa()){
d = *min_element(dis + 1, dis + N + 1);
printf("Case %d:\n", kase);
for(i = 1; i <= N; ++i) printf("%d %d\n", i, dis[i] - d);
puts("");
}
else printf("Case %d:\nimpossible\n\n", kase);
}
return 0;
}