【差分约束系统】 layout奶牛的站位

原作者博客

描述

有 N 头奶牛正在排队,它们的编号为 1 到 N,约翰要给它们安排合适的排队位置,满足以下条 件:

• 首先,所有奶牛要站在一条直线上。由于是排队,所以编号小的奶牛要靠前,不能让编号大的 奶牛插队。但同一个位置可以容纳多头奶牛,这是因为它们非常苗条的缘故

• 奶牛喜欢和朋友靠得近点。朋友关系有 F 对,其中第 Ai 头奶牛和第 Bi 头奶牛是第 i 对朋友, 它们的距离不能超过 Ci

• 奶牛还要和讨厌的同类保持距离。敌对关系有 E 对,其中第 Xi 头奶牛和第 Yi 头奶牛是第 i 对 敌人,它们的距离不能少于 Zi

你能否帮助约翰找到一个合理的站位方法,满足所有奶牛的要求,而且让 1 号奶牛和 N 号奶牛间的 距离尽量大

输入

第一行:三个整数 N, F 和 E, 2 ≤ N ≤ 1000, 1 ≤ F; E ≤ 10000

• 第二行到第 F + 1 行:第 i + 1 行有三个整数 Ai, Bi 和 Ci, 1 ≤ Ai; Bi ≤ N; 1 ≤ Ci ≤ 1e6

• 第 F + 2 行到第 F + E + 1 行:第 i + F + 1 行有三个整数 Xi, Yi 和 Zi, 1 ≤ Xi; Yi ≤ N; 1 ≤ Zi ≤ 10^6

输出

单个整数:如果不存在满足所有条件的安排,输出 −1;如果 1 号奶牛和 N 号奶牛的距离可以 任意大,输出 −2;否则输出 1 号奶牛和 N 号奶牛的最大距离

样例输入
4 2 1
1 3 10
2 4 20
2 3 3
样例输出
27
提示

一号奶牛站在坐标 0,二号奶牛站在坐标 7, 三号奶牛站在坐标 10,四号奶牛站在坐标 27
Jun 添加一组hack数据

解析:

根据差分约束,可得:

  • 若是有数据Ai,Bi,Ci,则可将其转变为图的 | dis[Ai] - dis[Bi] | <= Ci,若确定Ai > Bi,则变形得:dis[Ai] <=Ci+dis[Bi],就可建一条从Bi到Ai权值为Ci的边来维护该条件
  • 若是有数据Xi,Yi,Zi,则可将其转变为图的 | dis[Xi] - dis[Yi] | >= Zi,若确定Xi > Yi,则变形得:dis[Yi]<=dis[Xi]-Zi,就可建一条边从Xi到Yi权值为Ci的边来维护该条件
  • 最后用SPFA来判断负环,并以1为起点跑一遍最短路,若n节点的值未更新,则输出-2
CODE
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e3+10,maxm=3e4+10;
struct road{
	int v,nxt,w;
}r[maxm];
int n,F,E;
int head[maxn],cnt=0;
inline void _add(int u,int v,int w){
	r[++cnt]=(road){v,head[u],w};
	head[u]=cnt;
}
int amt[maxn];
int dis[maxn];
bool vis[maxn];
inline bool spfa(int st){
	queue<int> q;
	dis[st]=0;
	q.push(st);
	while(!q.empty()){
		int u=q.front();q.pop();
		vis[u]=0;
		for(int i=head[u];i;i=r[i].nxt){
			int v=r[i].v,w=r[i].w;
			if(dis[v]>dis[u]+w){
				dis[v]=dis[u]+w;
				++amt[v];//若被修改了超过n次则必有负环 
				if(amt[v]>n)return 0;
				if(!vis[v])q.push(v),vis[v]=1;
			}
		}
	}
	return 1;
}
int a,b,c;
signed main(){
	scanf("%d%d%d",&n,&F,&E);
	for(int i=1;i<=F;++i){
		scanf("%d%d%d",&a,&b,&c);
		if(a<b)swap(a,b);
		_add(b,a,c);
	}
	for(int i=1;i<=E;++i){
		scanf("%d%d%d",&a,&b,&c);
		if(a<b)swap(a,b);
		_add(a,b,-c);//由于是根据dis[b->a]>=c推出dis[a->b]<=-c,则原条件始终满足故不存在dis[i]<0; 
	}
	memset(dis,0x5f,sizeof dis);
	memset(vis,0,sizeof vis);
	//注意判无解 要每一个点都走一遍 
	for(int i=1;i<=n;++i)if(dis[i]==0x5f5f5f5f){//若所给的图未联通,跑每一个点来确定所有连通图内都无负环 
		if(!spfa(i)){
			printf("-1");return 0;
		}
	}
	memset(dis,0x5f,sizeof dis);
	memset(vis,0,sizeof vis);
	if(spfa(1)){
		if(dis[n]==0x5f5f5f5f){
			printf("-2");
		}
		else printf("%d",dis[n]);
	}
	else{
		printf("-1");
	}
	return 0;
}

自己写了一遍

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+5,M=1e4+5;
int read(){
	int s=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-f;
		ch=getchar();
	}
	while(isdigit(ch)){
		s=(s<<3)+(s<<1)+ch-'0';
		ch=getchar();
	}
	return s*f;
}
struct fjy{//
	int nxt,v,w;
}e[M<<1];
void swap(int &x,int &y){
	int c=x;x=y;y=c;
}
int cnt,fir[N],val[N],amt[N],dis[N];
void add(int u,int v,int w){
	e[++cnt]=(fjy){fir[u],v,w};
	fir[u]=cnt;
}
int n,E,F,ai,bi,ci;
bool spfa(int x){
	val[x]=1;dis[x]=0;
	queue<int> q;
	q.push(x);
	while(q.size()){
		int u=q.front();
		q.pop();val[u]=0;
		for(int i=fir[u];i;i=e[i].nxt){
			if(dis[e[i].v]>dis[u]+e[i].w){
				amt[e[i].v]++;
				if(amt[e[i].v]>n) return 0;
				dis[e[i].v]=dis[u]+e[i].w;
				if(!val[e[i].v]) val[e[i].v]=1,q.push(e[i].v);
			}
		}
	}
	return 1;
}
int main(){
	n=read(),F=read(),E=read();
	for(int i=1;i<=F;i++){
		ai=read(),bi=read(),ci=read();
		if(ai<bi)swap(ai,bi);
		add(bi,ai,ci);
	}
	for(int i=1;i<=E;i++){
		ai=read(),bi=read(),ci=read();
		add(ai,bi,-ci);
	}
	memset(dis,0x7f7f7f7f,sizeof dis);
	for(int i=1;i<=n;i++)
		if(dis[i]==0x7f7f7f7f)
		if(!spfa(i)) return printf("-1"),0;
	memset(val,0,sizeof val);
	memset(dis,0x7f7f7f7f,sizeof dis);
	if(!spfa(1)) printf("-1");
	else if(dis[n]=0x7f7f7f7f) printf("-2");
	else printf("%d",dis[n]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值