Layout「差分约束」
题目描述
和人类一样,奶牛们在打饭的时候喜欢和朋友站得很近。约翰的编号为 1
到 n
的 n
(2
<=n
<=1000
) 只奶牛正打算排队打饭。现在请你来安排她们,让她们在数轴上排好队。奶牛的弹性很好,同一个坐标可以站无限只奶牛,排队的顺序必须和她们编号的顺序一致。有 M
对奶牛互相爱慕,她们之间的距离不能超过一定的值,有 K
对奶牛互相敌视,她们的距离不能小于一定的值 那么,首尾奶牛的最大距离是多少呢?
输入
第一行输入 n
,M
,K
,(0
<M
,K
<=5000
)。
接下来 M
行每行三个整数x
,y
,z
,表示编号为 x
和 y
的两头奶牛之间的距离最大不超过 z
。
再接下来 K
行每行三个整数 a
,b
,c
,表示编号为 a
和 b
的两头奶牛之间的距离最少为c
。
输出
如果没有合理方案,输出 −1
,如果首尾两头牛的距离可以无限大,输出 −2
,否则输出一个整数表示首尾奶牛的最大距离。
样例
样例输入
4 2 1
1 3 10
2 4 20
2 3 3
样例输出
27
思路分析
裸的差分约束,已经忘得一干二净
- 通过题目中给出的信息显然我们可以得出形如xi - xj < a的关系,这样就成了一道裸的差分约束的题
- 另外这题还需要建立一个超级原点,来判断图是否联通或有无负权环,因为1可能无法到达所有点
差分约束基本思路
-
给定n个变量和m个不等式,每个不等式形如xi−xj≤w,求xn−1−x0 的最大值。(0 <= i, j < n)
-
举个栗子:
x2
−x1
≤a1
x3
−x2
≤a2
x4
−x3
≤a3
x5
−x4
≤a4
比方说我们有以上几个不等式,求 x5
−x1
的最大值,直接代入计算就可以,但是要计算机能拿来计算,我们得有一个规矩点的方法
我们可以通过移项,发现 xi
≤xj
+ak
然后可以自然而然地想到求最短路时的松弛操作(xi被松弛,作为边的终点),但是这里符号是反的,这并无大碍,因为我们是求 x5
−x1
的最大值,就等于 x1
~x5
的最小值(因为要保证所有不等式都成立,同小取小),如果 dis[n] 是无穷大,就表明 x5
−x1
可以无穷大,如果出现负环,就表明无解
- 因为存在负环,所以我们用spfa来跑最短路
代码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
int n,m,k;
const int maxn = 1e3+10,maxm = 2e4+10,inf = 0x3f3f3f3f;
int head[maxn],dis[maxn],vis[maxn],cnt[maxn];
struct edge{
int to,next,w,flag;
}e[maxm];
int len = 0;
void addedge(int u,int v,int w){
e[++len].to = v;
e[len].w = w;
e[len].next = head[u];
head[u] = len;
}
int spfa(int x){
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
memset(cnt,0,sizeof(cnt));
queue<int>q;
dis[x]=0;
q.push(x);
while(!q.empty()){
int u = q.front();q.pop();
vis[u]=0;
for(int i = head[u];~i;i = e[i].next){
int v = e[i].to,w = e[i].w;
if(dis[v]>dis[u]+w){
dis[v] = dis[u]+w;
if(!vis[v]){
vis[v] = 1;
q.push(v);
cnt[v]++; //cnt记录松弛次数
if(cnt[v]>n)return -1; //存在负环,无解
}
}
}
}
if(dis[n]==inf)return -2;//未被松弛,说明距离可以无限大
return dis[n]; //x1-xn的最小值,即为xn-x1的最大值
}
int main(){
scanf("%d%d%d",&n,&m,&k);
memset(head,-1,sizeof(head));
for(int i = 1;i <= m;i++){
int u,v,w;scanf("%d%d%d",&u,&v,&w);
if(u>v)swap(u,v);
addedge(u,v,w);
}
for(int i = 1;i <= k;i++){
int u,v,w;scanf("%d%d%d",&u,&v,&w);
if(u>v)swap(u,v);
addedge(v,u,-w); //这里由原来的xi-xj>=a改为xj-xi<=-a,即xj<=-a+xi,那么我们就从v到u反过来建一条权值为负的边
}
for(int i = 1;i <= n;i++){
addedge(i+1,i,0); //这题的一个坑,相邻节点要建边,因为牛必须按编号相邻
}
for(int i = 1;i <= n;i++){
addedge(0,i,0); //建一个超级原点保证与所有节点相连
}
if(spfa(0)==-1){ //有无解从超级原点开始判断
printf("-1");
return 0;
}
printf("%d",spfa(1));//有解,求最大值
return 0;
}