POJ3169 差分約束
題目:
LayoutTime Limit: 1000MS | Memory Limit: 65536K |
Total Submissions: 13445 | Accepted: 6447 |
Description
Like everyone else, cows like to stand close to their friends when queuing for feed. FJ has N (2 <= N <= 1,000) cows numbered 1..N standing along a straight line waiting for feed. The cows are standing in the same order as they are numbered, and since they can be rather pushy, it is possible that two or more cows can line up at exactly the same location (that is, if we think of each cow as being located at some coordinate on a number line, then it is possible for two or more cows to share the same coordinate).Some cows like each other and want to be within a certain distance of each other in line. Some really dislike each other and want to be separated by at least a certain distance. A list of ML (1 <= ML <= 10,000) constraints describes which cows like each other and the maximum distance by which they may be separated; a subsequent list of MD constraints (1 <= MD <= 10,000) tells which cows dislike each other and the minimum distance by which they must be separated.
Your job is to compute, if possible, the maximum possible distance between cow 1 and cow N that satisfies the distance constraints.
Input
Lines 2..ML+1: Each line contains three space-separated positive integers: A, B, and D, with 1 <= A < B <= N. Cows A and B must be at most D (1 <= D <= 1,000,000) apart.
Lines ML+2..ML+MD+1: Each line contains three space-separated positive integers: A, B, and D, with 1 <= A < B <= N. Cows A and B must be at least D (1 <= D <= 1,000,000) apart.
Output
Sample Input
4 2 1 1 3 10 2 4 20 2 3 3
Sample Output
27
題意:
n頭牛編號1~n。按照編號順序排成一排。有ML對關係好的牛的資訊,(AL,BL,DL)。有MD對關係差的牛的資訊,(AD,BD,DD)。要求牛AL與牛BL之間的最大距離為DL,牛AD與牛BD之間的最大距離為DD。在滿足這些條件的方法中,求1號牛與n號牛的最大距離。不存在輸出-1,無限大輸出-2。
方法:差分約束(摘自點選開啟連結)
一、何為差分約束系統:
差分約束系統(system of difference constraints),是求解關於一組變數的特殊不等式組之方法。如果一個系統由n個變數和m個約束條件組成,其中每個約束條件形如xj-xi<=bk(i,j∈[1,n],k∈[1,m]),則稱其為差分約束系統(system of difference constraints)。亦即,差分約束系統是求解關於一組變數的特殊不等式組的方法。
通俗一點地說,差分約束系統就是一些不等式的組,而我們的目標是通過給定的約束不等式組求出最大值或者最小值或者差分約束系統是否有解。
比如:
二、差分約束系統的求解:
差分約束系統可以轉化為圖論來解決,對應於上面的不等式組,如果要求出x3-x0的最大值的話,疊加不等式可以推匯出x3-x0<=7,最大值即為7,我們可以通過建立一個圖,包含6個頂點,對每個xj-xi<=bk,建立一條i到j的有向邊,權值為bk。通過求出這個圖的x0到x3的最短路可以知道也為7,這是巧合嗎?並不是。
之所以差分約束系統可以通過圖論的最短路來解,是因為xj-xi<=bk,會發現它類似最短路中的三角不等式d[v] <=d[u]+w[u,v],即d[v]-d[u]<=w[u,v]。而求取最大值的過程類似於最短路演算法中的鬆弛過程。
三角不等式:(在此引用大牛的部落格)
B - A <= c (1)
C - B <= a (2)
C - A <= b (3)
如果要求C-A的最大值,可以知道max(C-A)= min(b,a+c),而這正對應了下圖中C到A的最短路。
因此,對三角不等式加以推廣,變數n個,不等式m個,要求xn-x1的最大值,便就是求取建圖後的最短路。
同樣地,如果要求取差分約束系統中xn-x1的最小值,便是求取建圖後的最長路。最長路可以通過spfa求出來,只需要改下鬆弛的方向即可,即if(d[v] < d[u] + dist(u,v)) d[v] = d[u] + dist(u,v)。當然我們可以把圖中所有的邊權取負,求取最短路,兩者是等價的。
最長路求解演算法證明如下:
最後一點,建圖後不一定存在最短路/最長路,因為可能存在無限減小/增大的負環/正環,題目一般會對應於不同的輸出。判斷差分約束系統是否存在解一般判環即可。
3、差分約束系統的應用
差分約束系統的應用很廣,都會有一定的背景,我們只需要根據題意構造出差分約束系統,然後再根據題目的要求求解就行了。
一般題目會有三種情況:(1)、求取最短路 (2)、求取最長路 (3)、判斷差分約束系統的解是否存在
當然這三種也可能會相互結合。
差分約束系統的解法如下:
1、 根據條件把題意通過變數組表達出來得到不等式組,注意要發掘出隱含的不等式,比如說前後兩個變數之間隱含的不等式關係。
2、 進行建圖:
首先根據題目的要求進行不等式組的標準化。
(1)、如果要求取最小值,那麼求出最長路,那麼將不等式全部化成xi – xj >= k的形式,這樣建立j->i的邊,權值為k的邊,如果不等式組中有xi – xj > k,因為一般題目都是對整形變數的約束,化為xi – xj >= k+1即可,如果xi – xj = k呢,那麼可以變為如下兩個:xi – xj >= k, xi – xj <= k,進一步變為xj – xi >= -k,建立兩條邊即可。
(2)、如果求取的是最大值,那麼求取最短路,將不等式全部化成xi – xj <= k的形式, 這樣建立j->i的邊,權值為k的邊,如果像上面的兩種情況,那麼同樣地標準化就行了。
(3)、如果要判斷差分約束系統是否存在解,一般都是判斷環,選擇求最短路或者最長路求解都行,只是不等式標準化時候不同,判環地話,用spfa即可,n個點中如果同一個點入隊超過n次,那麼即存在環。
值得注意的一點是:建立的圖可能不聯通,我們只需要加入一個超級源點,比如說求取最長路時圖不聯通的話,我們只需要加入一個點S,對其他的每個點建立一條權值為0的邊圖就聯通了,然後從S點開始進行spfa判環。最短路類似。
3、 建好圖之後直接spfa或bellman-ford求解,不能用dijstra演算法,因為一般存在負邊,注意初始化的問題。
本題中存在3種約束:第一種為牛的排列:d[i]<=d[i+1] ;
第二種為關係好的牛所限制的最大距離:d[AL] + DL >=d[BL];
第三種為關係差的牛限制的最小距離:d[AD] + DD <=d[BD]。
題目中求1號牛到n號牛的最大值,即求1到n的最短路。
變型不等式為最短路鬆弛形式操作得:d[i+1]+0 >= d[i] , d[AL]+DL >=d[BL] , d[BD]-DD >= d[AD]。
然後根據所有不等式建圖,跑SPFA即可。
若SPFA過程中判出負環,則無解。
若結果最短路d[n] 為INF,則無約束,1到n的距離可以無限大。
程式碼:
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<stack>
#define sd(x) scanf("%d",&x)
#define ss(x) scanf("%s",x)
#define sc(x) scanf("%c",&x)
#define sf(x) scanf("%f",&x)
#define slf(x) scanf("%lf",&x)
#define slld(x) scanf("%lld",&x)
#define me(x,b) memset(x,b,sizeof(x))
#define pd(d) printf("%d\n",d);
#define plld(d) printf("%lld\n",d);
// #define Reast1nPeace
typedef long long ll;
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 10010;
int n,ml,md;
int k;
struct edge{
int to,weight,next;
}G[maxn*2];
int head[maxn];
int d[maxn];
bool vis[maxn];
int num[maxn];
void spfa(){
queue<int> q;
memset(d,INF,sizeof(d));
memset(num,0,sizeof(num));
memset(vis,0,sizeof(vis));
q.push(1);
vis[1] = 1;
d[1] = 0;
num[1]++;
while(!q.empty()){
int now = q.front(); q.pop();
for(int i = head[now] ; i!=0 ; i = G[i].next){
if(d[now] + G[i].weight < d[G[i].to] ){
d[G[i].to] = d[now]+G[i].weight;
if(!vis[G[i].to]){
q.push(G[i].to);
vis[G[i].to] = 1;
num[G[i].to]++;
if(num[G[i].to] >= n){
cout<<"-1"<<endl;
return;
}
}
}
}
vis[now] = 0;
}
if(d[n] == INF){
cout<<-2<<endl;
return;
}
cout<<d[n]<<endl;
}
int main(){
#ifdef Reast1nPeace
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
ios::sync_with_stdio(false);
memset(head,0,sizeof(head));
memset(G,0,sizeof(G));
cin>>n>>ml>>md;
int a,b,d;
k = 1;
for(int i = 2 ; i<=n ; i++){
G[k].to = i-1;
G[k].weight = 0;
G[k].next = head[i];
head[i] = k;
k++;
}
for(int i = 1 ; i<=ml ; i++){
cin>>a>>b>>d;
G[k].to = b;
G[k].weight = d;
G[k].next = head[a];
head[a] = k;
k++;
}
for(int i = 1 ; i<=md ; i++){
cin>>a>>b>>d;
G[k].to = a;
G[k].weight = -d;
G[k].next = head[b];
head[b] = k;
k++;
}
spfa();
return 0;
}