差分約束-Vijos-p1094

還沒有對差分約束有了解的同學我推薦一篇大牛的博客,它的博客都講的比較好。

博客鏈接:夜深人靜寫算法-差分約束

上面的大神講的很詳細,相信認真看了就會懂。
我說說我對差分約束的理解。我感覺差分約束就是將問題轉換成一些不等式組,然後通過這些不等式組建邊,最後就轉換成求最短路、最長路、最大值、最小值等問題。(個人理解)

現在我以Vijos的p1094爲例(感覺是一道不錯的模版題)

題目鏈接:https://vijos.org/p/1094

題意
就是給出一些節點的大小關係(>,<,=),讓你求出最小的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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章