1. 程式人生 > 實用技巧 >P4878 [USACO05DEC]Layout G 差分約束

P4878 [USACO05DEC]Layout G 差分約束

題目大意

  • 和人類一樣,奶牛們在打飯的時候喜歡和朋友站得很近。
  • 約翰的編號為 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

演算法分析

  • 不會吧不會吧不會真的有人看不出來這是差分約束吧
  • 然後就是一個差分約束的板子題了
  • 建邊跑最短路(求最大值)
  • 建超級源點

演算法展示

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3+10,maxm = 2e4+10;
const int Inf = 0x3f3f3f3f;
int n;
int head[maxn],cnt;
int num[maxn],dis[maxn],vis[maxn];

struct node{
	int next,to,dis;
}a[maxm];

void add(int x,int y,int z){
	a[++cnt].to = y;
	a[cnt].next = head[x];
	a[cnt].dis = z;
	head[x] = cnt;
}

queue<int> q;

int spfa(int u){
	memset(dis,0x3f,sizeof(dis));
	memset(vis,0,sizeof(vis));
	memset(num,0,sizeof(num));
	q.push(u);vis[u] = 1;
	dis[u] = 0;
	while(!q.empty()){
		int u = q.front();q.pop();vis[u] = 0;
		for(int i = head[u];i;i = a[i].next){
			int v = a[i].to;
			if(dis[v] > dis[u] + a[i].dis){
				dis[v] = dis[u] + a[i].dis;
				if(!vis[v]){
					vis[v] = 1;q.push(v);
					num[v]++;
					if(num[v] > n)return -1;//判負環
				}
			}
		}
	}
	if(dis[n] == Inf)return -2;//如果無限大
	return dis[n];
}

int main(){
	int m,k;scanf("%d%d%d",&n,&m,&k);
	for(int i = 1;i <= m;++i){
		int x,y,z;scanf("%d%d%d",&x,&y,&z);
		if(x > y)swap(x,y);
		add(x,y,z);
	}
	for(int i = 1;i <= k;++i){
		int x,y,z;scanf("%d%d%d",&x,&y,&z);
		if(x > y)swap(x,y);
		add(y,x,-z);//建邊
	}
	for(int i = 1;i < n;++i)add(i+1,i,0);//建邊 
	for(int i = 1;i <= n;++i)add(0,i,0);//超級源點
	if(spfa(0) == -1){printf("-1\n");return 0;}//超級源點為起點跑spfa 防止有的負環無法到達的情況
	printf("%d\n",spfa(1));
	return 0;
}

謝謝觀看>:<