Layout「差分约束」

Layout「差分约束」

题目描述

和人类一样,奶牛们在打饭的时候喜欢和朋友站得很近。约翰的编号为 1nn(2<=n<=1000) 只奶牛正打算排队打饭。现在请你来安排她们,让她们在数轴上排好队。奶牛的弹性很好,同一个坐标可以站无限只奶牛,排队的顺序必须和她们编号的顺序一致。有 M 对奶牛互相爱慕,她们之间的距离不能超过一定的值,有 K对奶牛互相敌视,她们的距离不能小于一定的值 那么,首尾奶牛的最大距离是多少呢?

输入

第一行输入 n,M,K,(0<M,K<=5000)。

接下来 M行每行三个整数x,y,z,表示编号为 xy 的两头奶牛之间的距离最大不超过 z

再接下来 K行每行三个整数 a,b,c,表示编号为 ab 的两头奶牛之间的距离最少为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)

  • 举个栗子:

    x2x1a1

    x3x2a2

    x4x3a3

    x5x4a4

比方说我们有以上几个不等式,求 x5x1的最大值,直接代入计算就可以,但是要计算机能拿来计算,我们得有一个规矩点的方法

我们可以通过移项,发现 xixj+ak 然后可以自然而然地想到求最短路时的松弛操作(xi被松弛,作为边的终点),但是这里符号是反的,这并无大碍,因为我们是求 x5x1 的最大值,就等于 x1~x5 的最小值(因为要保证所有不等式都成立,同小取小),如果 dis[n] 是无穷大,就表明 x5x1 可以无穷大,如果出现负环,就表明无解

  • 因为存在负环,所以我们用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;
}

原文地址:https://www.cnblogs.com/hhhhalo/p/13323954.html