新英雄 二分答案 差分約束

新英雄

問題描述

老張也準備沉迷於lol不能自拔。爲了表示自己的誠意,老張設計了一個新英雄。這個新英雄的大招非常強勢,在追人的時候能體現非常強的優勢。假設召喚師峽谷是一個有n個節點,m條單向邊的圖。對於每一個節點x,可以把所有以x爲終點的邊的權值減少d(-10000<=d<=10000),同時把所有以x爲起點的邊的權值加上d。要讓所有邊的權值的最小值最大。當然,邊的權值不能爲零或負,因爲這不符合召喚師峽谷的物理規律。

輸入格式

有多組數據,對於每一組數據:
第一行爲兩個整數n,m
接下來m行,每一行三個整數a,b,c 表示從a到b有一條長度爲c的道路

輸出格式

對於每一個數據塊輸出文件僅有一行:
如果答案有且僅有一個解,輸出最短道路的最大可能值
如果答案具有任意性,即有多解,輸出”Infinite”
如果無解,輸出”No Solution”

樣例輸入

2 1
1 2 10
2 1
1 2 -10
3 3
1 2 4
2 3 2
3 1 5
4 5
2 3 4
4 2 5
3 4 2
3 1 0
1 2 -1

樣例輸出

Infinite
Infinite
3
1

數據規模

n≤500,m≤2700,-10000<=d<=10000每條道路的長度保證不超過10000


“最小值最大”,二分答案標誌。顯然,如果有解而且有有限解,那麼答案一定在[1,10000]內。現在考慮如何驗證答案。

設當前檢驗的答案爲ans ,在p 號點操作的總權值爲d[p] ,那麼對於每一條邊都滿足以下不等式:

ansd[x]d[y]+len[x][y]

移項後得到差分約束的標準形式:
d[y]d[x]+len[x][y]ans

所以如果當前答案都能滿足所有這樣的不等式,那就是合法的,反之就不是。也就是說,這樣建圖後,如果有負權迴路就是不合法的,反之就是合法的。所以採用SPFA統計入隊次數的方法。

那麼什麼情況下無解呢?如果原圖中就有負環就無解。
什麼情況下有多解呢?驗證10001或更大的一個數,如果合法就說明有多解。

需要注意,每組數據先判斷無解和無數組解的情況會比較好,特別是先判是否無解,因爲如果無解,那麼二分答案時,每次SPFA都要跑很多次使得入隊次數達到(N+1)


代碼:

#include<stdio.h>
#include<cstring>
#define MAXN 505
#define MAXM 6005
using namespace std;

int N,M;

int en[MAXM],nex[MAXM],las[MAXN],len[MAXM],tot;
void Add(int x,int y,int z)
{
    en[++tot]=y;
    nex[tot]=las[x];
    las[x]=tot;
    len[tot]=z;
}

int Cnt[MAXN],Dis[MAXN];
bool mark[MAXN];
int Q[1000005],head,tail;

bool SPFA(int s,int k)
{
    memset(Cnt,0,sizeof(Cnt));
    memset(Dis,60,sizeof(Dis));
    memset(mark,false,sizeof(mark));

    Dis[0]=0;
    head=1;
    tail=0;

    int i,x,y;
    Q[++tail]=s;
    while(head<=tail)
    {
        x=Q[head];head++;mark[x]=false;
        for(i=las[x];i;i=nex[i])
        {
            y=en[i];
            if(Dis[y]>Dis[x]+len[i]-k)
            {
                Dis[y]=Dis[x]+len[i]-k;
                if(!mark[y])
                {
                    mark[y]=true;
                    Cnt[y]++;
                    if(Cnt[y]>N)return false;
                    Q[++tail]=y;
                }
            }
        }
    }
    return true;
}

void Init()
{
    memset(las,0,sizeof(las));
    tot=0;
}

int main()
{
    int i,x,y,z;

    while(scanf("%d%d",&N,&M)!=EOF)
    {
        Init();

        for(i=1;i<=M;i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            Add(x,y,z);
        }

        for(i=1;i<=N;i++)Add(0,i,0);

        if(!SPFA(0,1))
        {
            puts("No Solution");
            continue;
        }
        if(SPFA(0,10001))
        {
            puts("Infinite");
            continue;
        }

        int L=1,R=10000,mid;
        while(L<=R)
        {
            mid=L+R>>1;
            if(SPFA(0,mid))L=mid+1;
            else R=mid-1;
        }
        printf("%d\n",R);
    }
}
發佈了105 篇原創文章 · 獲贊 23 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章