差分約束系統詳解(hdu1531 King爲例)

        嗯,這兩天用空閒時間把差分約束系統學習了一下。話說暑假已經快結束了,在忙碌的時候,依然悠哉悠哉地學算法真的好嗎。。。算了,學都已經學了,就把它放到網上作紀念吧(纔不是強迫症呢,哼!)。因爲是剛學的,所以有很多地方可能理解不當,希望大神們多加指教。


        差分約束系統,就是將不等式的約束問題,抽象成最短路問題的一種方法,屬於線性規劃的一種。所以在此之前至少要熟練掌握Bellman-ford算法或者spfa算法的其中一種。Dijkstra是不行的,因爲要處理負權邊的問題。


        首先我們先看看如何把不等式的約束問題轉化爲圖論的最短路問題。假設有這樣的約束條件:S[3]-S[0]<=k1, S[5]-S[3]<=k2,那麼兩式相加,則可以得出S[5]-S[0]<=k1+k2。可以抽象成,從節點0到3的距離爲k1,從節點3到節點5的距離爲k2.因爲要滿足所有的約束條件,對於<=這個關係,我們只需要求出0到5的最短路,最短路的長度,就是S[0]-S[5]的最大值。如果是>=這個關係,則需要求出最長路,最長路的長度,就是S[5]-S[0]的最小值。對於任意的S[i]-S[j]<=ki也可以根據上述條件很輕鬆地建圖。如果不等式的關係不一致怎麼辦?比如S[i]-S[j]>=k1, S[k]-S[j]<=k2.那麼可以對於其中的一個不等式,兩邊同時乘以-1,可以得到S[j]-S[k]>=-k2,然後建圖求解。


        另外,不等式除了有一個解的情況,還有無數個解的情況和無解的情況,這又該如何判斷呢?這裏,在下就直接說方法了,不加證明了(拿特殊值代入法也能幫助理解)。當圖中存在負環的時候,無解。當最後一個節點沒有被更新的時候,有無窮多個解。


        差分約束系統介紹完了,下面我們來看例題hdu1531.

        hdu1531這一題,赤裸裸的差分約束系統的題目。就是國王給出一堆不等式,然後判斷這個不等式所滿足的數列是否存在,如果存在則輸出lamentable king,如果不存在,則輸出successful conspiracy。好的,下面來說明這道題。


        因爲這道題沒有讓你輸出解,只讓你輸出是否有解,題目就變得更加簡單了。設那個數列a1+a2+a3+...+an=Sn可以獲得題目上的條件有兩種可能,①S[si+ni]-S[si-1]>ki,②S[si+ni]-S[si-1]<ki。因爲是<和>符號,我們需要把它換成>=和<=,怎麼辦呢?直接對於ki操作即可,變成>=ki+1,<=ki-1。最後變形完成可得:①S[si-1]-S[si+ni]<=-ki-1,②S[si+ni]-S[si-1]<=ki-1。然後直接建圖,從0到n過一遍spfa或者bellman-ford就可以了,如果有負環,則輸出successful conspiracy,如果沒有則輸出lamentable king。但是需要注意的是,要使整個圖聯通才可以,所以將0節點設爲虛點,我們在這裏叫他超級節點。這個節點與所有點連通,權值爲小於dist的初值的無窮大。我這裏做的是求最短路。還有一點需要注意的是,因爲是從0到n所以有n+1個節點,所以入隊次數大於n+1時,才能被判斷是負環,也算是一個小細節了,下面上代碼:


代碼君:

#include <cstdio>
#include <algorithm>
#include <string>
#include <cstring>
using namespace std;
typedef struct
{
    int x,y,z;
}atp;
atp line[220];
int s[110],nn[110],kk[110],found[110],dist[110],hash[110],flag,n,m,d[5000];
char o[110][110];

bool cmp(atp a,atp b)
{
    return (a.x<b.x);
}

void spfa()
{
    int f,r,vis[110],i;
    f=1;r=1;
    memset(vis,0,sizeof(vis));
    d[f]=0; dist[0]=0; vis[0]=1;
    while(f<=r)
    {
        for(i=found[d[f]];line[i].x==d[f]&&i<=m+n;i++)
        {
            if(dist[line[i].y]>dist[line[i].x]+line[i].z)
            {
                dist[line[i].y]=dist[line[i].x]+line[i].z;
                if(!vis[line[i].y])
                {
                    if(hash[line[i].y]>n+1)  //要注意是>n+1而不是n
                    {
                        flag=1;
                        return;
                    }
                    r++;
                    vis[line[i].y]=1;
                    d[r]=line[i].y;
                    hash[line[i].y]++;
                }
            }
        }
        vis[d[f]]=0;
        f++;
    }
}

int main()
{
    int i,j,k;
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0) break;
        scanf("%d",&m);
        for(i=1;i<=m;i++)
            scanf("%d%d%s%d",&s[i],&nn[i],o[i],&kk[i]);
        k=0;
        for(i=m+1;i<=m+n+1;i++)
        {
            line[i].x=0;
            k++;
            line[i].y=k;
            line[i].z=10000000;
        }
        for(i=1;i<=m;i++)
        {
            if(strcmp(o[i],"gt")==0)
            {
                line[i].x=s[i]+nn[i];
                line[i].y=s[i]-1;
                line[i].z=-kk[i]-1;
            }
            if(strcmp(o[i],"lt")==0)
            {
                line[i].x=s[i]-1;
                line[i].y=s[i]+nn[i];
                line[i].z=kk[i]-1;
            }
        }
        k=1;
        for(i=0;i<=100;i++)
            dist[i]=2147483647;
        sort(line+1,line+m+n+1,cmp);
        line[0].x=-1;
        for(i=1;i<=m+n;i++)
        {
            if(line[i].x!=line[i-1].x)
                found[line[i].x]=i;
        }
        memset(hash,0,sizeof(hash));
        flag=0;
        spfa();
        if(flag==1)
            printf("successful conspiracy\n");
        else
            printf("lamentable kingdom\n");
    }

    return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章