1. 程式人生 > >NOIP模擬賽(by hzwer) T3 小奇回地球

NOIP模擬賽(by hzwer) T3 小奇回地球

n) pre 時空隧道 超時 ostream 最小 禁止 我們 ++

【題目背景】

  開學了,小奇在回地球的路上,遇到了一個棘手的問題。

【問題描述】

  簡單來說,它要從標號為 1 的星球到標號為 n 的星球,某一些星球之間有航線。 由於超時空隧道的存在,從一個星球到另一個星球時間可能會倒流,而且,從星 球 a 到 b 耗費的時間和星球 b 到 a 耗費的時間不一定相同。宇宙法規定:“禁止在出發時間前到達目的地”。每艘飛船上都有速度調節裝置,可以調節飛行的時間。其功能可以使得整次航程中所有兩星球間的飛行時間增加或減少相同的整數值。你的任務是幫助它調整速度調節器,找出一條最短時間到達目的地的路徑。

【輸入格式】

  輸入文件包含多組數據,第 1 個數為 T,表示數據組數。對於每組數據,輸入第 1 行為兩個正整數 n,m,為星球的個數和星球間的路線數。接下來 m 行,每行三個整數 i,j 和 t,表示由星球 i 到星球 j 飛行的時間為 t。由 i 到 j 最多只會有一條飛行線路。

【輸出格式】

  輸出文件共 T 行,每組數據輸出一行。

  如果可以通過調節速度調節器完成任務,則輸出一個非負整數,表示由星球1到星球 n 的最短時間。(註意最短時間要大於或者等於 0)。如果不能由星球 1 到達星球 n,則輸出-1。

【樣例輸入】

  1

  4 5

  1 2 1

  1 3 1

  2 3 -3

  3 1 1

  3 4 1

【樣例輸出】

  2

【樣例解釋】

  把速度控制器的值設為 1,相當於每個時間值加 1,得到的最短路徑為 1→2→3→4。所需時間為 2+(-2)+2=2。

【數據範圍】

  1,2 號測試點,保證所有星球出度不超過1

  3,4 號測試點,n<=10

  5,6 號測試點,-100<=t<=100

  對於 100%的數據 T<=10,n<=100,m<=n*(n-1),-100000<=t<=100000

  數據隨機和構造結合生成

【解析】

  將此題簡化後可得如下模型:給定一張有向圖(有負邊權),可以使每一條邊加上或減去一個值t,使從1到n的最短路徑最小且非負。

  經過分析可以知道,若給每一條邊加上一個值t0後,1到n的最短路為負,那麽對於任意t<t0都有最短路徑仍為負。由此可以想到二分答案。t的值域為-100000到100000,那麽二分的左右邊界就定好了。然後每次都用SPFA檢驗最短路徑是否大於等於0,然後......死循環了。

  為什麽呢?假設有t1

<0,那麽圖中就有幾率出現負權環,那麽就沒有最短路。所以要在SPFA中加入判斷負權環的內容。但即使這樣仍會超時。那麽我們繼續思考怎麽優化。顯然,如果一個點與1或n不連通,那麽它對答案是沒有貢獻的。我們先從1出發遍歷整張圖,把無法到達的點刪去。然後再從1能夠到達的點出發,如果該點不能到達n,也從集合中刪去。在“砍圖”之後,雖然時間已經優化了,但仍然不夠。題目中有一句話是這麽說的:

數據隨機和構造結合生成

  那是不是會卡SPFA呢?所以,一個神奇的操作就出來了:深度優先搜索版SPFA。用DFS-SPFA去判斷負權環即可。

【代碼】

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define N 102
#define M 200002
using namespace std;int head[N],ver[M],nxt[M],edge[M],c;
int t,n,m,i,cnt[N],dis[N];
bool e[N],vis[N],in[N];
queue<int> q;
int read()
{
    char c=getchar();
    int w=0,f=1;
    while(c<0||c>9){
        if(c==-) f=-1;
        c=getchar();
    }
    while(c<=9&&c>=0){
        w=w*10+c-0;
        c=getchar();
    }
    return w*f;
}
void insert(int x,int y,int z)
{
    c++;
    ver[c]=y;
    edge[c]=z;
    nxt[c]=head[x];
    head[x]=c;
}
bool dfs_SPFA(int x,int s)
{
    vis[x]=1;
    for(int i=head[x];i;i=nxt[i]){
        int y=ver[i];
        if(dis[y]>dis[x]+edge[i]+s&&e[y]){
            if(vis[y]) return 1;
            dis[y]=dis[x]+edge[i]+s;
            if(dfs_SPFA(y,s)) return 1;
        }
    }
    vis[x]=0;
    return 0;
}
void SPFA(int s)
{
    memset(dis,0x3f,sizeof(dis));
    memset(in,0,sizeof(in));
    q.push(1);
    in[1]=1;
    dis[1]=0;
    while(!q.empty()){
        int x=q.front();
        q.pop();
        for(int i=head[x];i;i=nxt[i]){
            int y=ver[i];
            if(dis[x]+edge[i]+s<dis[y]&&e[y]){
                dis[y]=dis[x]+edge[i]+s;
                if(!in[y]){
                    in[y]=1;
                    q.push(y);
                }
            }
        }
        in[x]=0;
    }
}
void dfs(int x)
{
    vis[x]=1;
    for(int i=head[x];i;i=nxt[i]){
        int y=ver[i];
        if(!vis[y]) dfs(y);
    }
}
bool check(int x)
{
    for(int i=1;i<=n;i++){
        if(e[i]){
            memset(dis,0x3f,sizeof(dis));
            memset(vis,0,sizeof(vis));
            if(dfs_SPFA(i,x)) return 0;
        }
    }
    SPFA(x);
    if(dis[n]>=0) return 1;
    return 0;
}
int main()
{
    freopen("earth.in","r",stdin);
    freopen("earth.out","w",stdout);
    cin>>t;
    while(t--){
        memset(e,1,sizeof(e));
        memset(head,0,sizeof(head));
        memset(vis,0,sizeof(vis));
        c=0;
        n=read();
        m=read();
        for(i=1;i<=m;i++){
            int u,v,w;
            u=read();
            v=read();
            w=read();
            insert(u,v,w);
        }
        dfs(1);
        for(i=1;i<=n;i++){
            if(!vis[i]) e[i]=0;
        }
        for(i=1;i<=n;i++){
            if(e[i]){
                memset(vis,0,sizeof(vis));
                dfs(i);
                if(!vis[n]) e[i]=0;
            }
        }
        int l=-100000,r=100000,mid,ans;
        while(l<=r){
            mid=(l+r)/2;
            if(check(mid)){
                ans=dis[n];
                r=mid-1;
            }
            else l=mid+1;
        }
        if(ans>1e9) cout<<"-1"<<endl;
        else cout<<ans<<endl;
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}

NOIP模擬賽(by hzwer) T3 小奇回地球