题意:
一群牛按照编号排列,但是个别之间有限制。有的不能挨得太近,有的不能离得太远。问第1头牛和第N头之间最远是多少?
思路:
最开始想着是贪心,但是发现不好贪,牛是按照序号排列的,但是关系却很复杂。之后参考了网上的思路知道是差分约束系统,但是自己理解起来还是换了一些功夫。
首先将题目中的关系均转化为不等式:
隐藏条件:
dis[i+1]>=dis[i]
限制条件:
dis[b]-dis[a]<=c
dis[b]-dis[a]>=c
目标结果:
dis[N]-dis[1]<=res
这些不等式的特点是所有式子的两边都只出现了一个变量。(差分约束系统)
我们这样理解:记从起点s出发,到各个顶点v的最短距离为dis[v]。因此,对于每条权值为w的边e=(v,u),都有dis[v]+w>=dis[u](因为最短路径不一定经过这条边)。所以在满足这些约束不等式的dis中,dis[v]-dis[s]的最大值就是从s到v的最短距离。
将这些不等式转换:
dis[i]-dis[i+1]<=0
dis[b]-dis[a]<=c
dis[a]-dis[b]<=-c
dis[N]-dis[1]<=res
则原问题转换成了一个图论中的最短路问题,0/c/-c转化为点与点之间的边的权值,e(i+1,i,0),e(a,b,c),e(b,a,-c)。因为边的权包含负值,我们选择Bellman或者SPFA来解决。
另外输出结果对应含负权最短路三种状态:
res=-1:图中包含负权回路,无解。(原题中的无解情况为对应点的相对位置和序号冲突,等价为负权回路的情况)(另:负权回路:一个环上的所有权值之和为一个负数)
res=-2:最短路的结果dis[N]=INF,N和1之间的距离可以无限大。
res=?:N与1之间的最短路,也就是N到1之间的最大距离。
代码实现:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
const int MAX_N = 1005;
const int INF = 0x7ffffff;
struct Edge{
int to;
int cost;
Edge( int to, int cost){
this->to = to;
this->cost = cost;
};
};
int N,ML,MD;
int cnt[MAX_N];
int dist[MAX_N];
bool mark[MAX_N];
vector<Edge> edge[MAX_N];
int spfa(int v);
int main()
{
while( scanf("%d%d%d",&N,&ML,&MD) != EOF ){
for( int i = 1; i <= N; i++ ){
cnt[i] = 0;
dist[i] = INF;
mark[i] = false;
edge[i].clear();
}
int a,b,c;
//注意这里只能是单向,因为牛是按照编号顺序排列的
for( int i = 0; i < ML; i++ ){
scanf("%d%d%d",&a,&b,&c);
edge[a].push_back(Edge(b,c));
}
for( int i = 0; i < MD; i++ ){
scanf("%d%d%d",&a,&b,&c);
edge[b].push_back(Edge(a,-c));
}
for( int i = 1; i < N; i++ ){
edge[i+1].push_back(Edge(i,0));
}
int res = spfa(1);
printf("%d\n",res);
}
return 0;
}
int spfa(int v){
queue<int> que;
que.push(v);
cnt[v]++;
dist[v] = 0;
mark[v] = true;
while( !que.empty() ){
v = que.front();
que.pop();
mark[v] = false;
int len = edge[v].size();
for( int i = 0; i < len; i++ ){
Edge tmp = edge[v][i];
if( dist[tmp.to] > dist[v]+tmp.cost ){
dist[tmp.to] = dist[v]+tmp.cost;
if( mark[tmp.to] == false ){
que.push(tmp.to);
cnt[tmp.to]++;
mark[tmp.to] = true;
//SPFA判断副环,某个节点进入队列大于N次
if( cnt[tmp.to] > N ){
return -1;
}
}
}
}
}
if( dist[N] == INF ){
return -2;
}
else{
return dist[N];
}
}