還沒有對差分約束有了解的同學我推薦一篇大牛的博客,它的博客都講的比較好。
博客鏈接:夜深人靜寫算法-差分約束
上面的大神講的很詳細,相信認真看了就會懂。
我說說我對差分約束的理解。我感覺差分約束就是將問題轉換成一些不等式組,然後通過這些不等式組建邊,最後就轉換成求最短路、最長路、最大值、最小值等問題。(個人理解)
現在我以Vijos的p1094爲例(感覺是一道不錯的模版題)
題意
就是給出一些節點的大小關係(>,<,=),讓你求出最小的k,使得所有節點填上[0,k]之間的數能滿足所有關係,不存在就輸出NO。
題解
感覺基本上算是裸的差分約束,直接不等式建邊。比如給出1>2即1-2>0即1-2>=1,所以可以建一條2指向1的權值爲1的邊。(這裏數字都是節點編號)同理,對於1<2可以建一條1指向2的權值爲1的邊,對於1=2可以建一條1到2的權值爲0的雙向邊,建完邊後就是一個圖。因爲我們要滿足所有的不等式,而不等式能經過一些變換得到新不等式,最後所有不等式基本都是a-b>=k的類型,所以我們求的就是所有不等式中k的最大值,對應的就是圖中的最長路。(因爲那個不等式滿足了,其它所有不等式都能找到對應值滿足,就像在圖中最長路上節點依次填入0-k,其它節點肯定能填入0-k之間的值使之滿足)
這裏就遇到一個問題了,因爲不知道起始點即源點,所以我們不好跑單源最長路。怎麼解決?想這種情況我也遇到過幾次了,一般可以自己新建一個點當源點,然後與其它節點之間添加一些合適的邊就可解決問題。對於這一題我是指定0作爲源點,然後與其它所有點之間建一條0指向它的邊即可。(不能是雙向邊,不然會出現正權環)最後SPFA跑一遍最長路就可以了。
#include <bits/stdc++.h>
using namespace std;
const int INF = -0x3f3f3f3f;
const int maxn = 1e3+5;
int n,m;
int dist[maxn],vis[maxn],inqueue_num[maxn];
struct Edge{
int to,len;
};
vector<Edge> edge[maxn];
void init()
{
memset(dist,INF,sizeof(dist));
memset(vis,0,sizeof(vis));
memset(inqueue_num,0,sizeof(inqueue_num));
for(int i=0;i<=n;i++) edge[i].clear();
}
bool SPFA()
{
int x;
dist[0]=0,vis[0]=1,inqueue_num[0]++;
queue<int>q;
q.push(0);
while(!q.empty())
{
x = q.front(),q.pop();
vis[x]=0;
for(int i=0;i<edge[x].size();i++)
{
int y=edge[x][i].to;
int len=edge[x][i].len;
if(dist[y]<dist[x]+len)
{
dist[y] = dist[x]+len;
if(!vis[y])
{
q.push(y);
vis[y]=1;
inqueue_num[y]++;
if(inqueue_num[y]>=n) return false;
}
}
}
}
return true;
}
int main()
{
init();
scanf("%d%d",&n,&m);
int u,v,c;
Edge tmp;
while(m--)
{
scanf("%d%d%d",&u,&v,&c);
if(c==1)
{
tmp.to=u,tmp.len=1;
edge[v].push_back(tmp);
}
else if(c==0)
{
tmp.to=v,tmp.len=0;
edge[u].push_back(tmp);
tmp.to=u,tmp.len=0;
edge[v].push_back(tmp);
}
else
{
tmp.to=v,tmp.len=1;
edge[u].push_back(tmp);
}
}
for(int i=1;i<=n;i++)
{
tmp.to=i,tmp.len=0;
edge[0].push_back(tmp);
}
if(SPFA())
{
int ans=INF;
for(int i=1;i<=n;i++) ans = max(ans,dist[i]);
printf("%d\n",ans);
}
else printf("NO\n");
return 0;
}