HDOJ-1534(差分约束)

题目: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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值