演算法學習之路|有向無環圖(DAG)求最長路

kissjz發表於2018-02-11

原題為2017暑假acm亞洲區預賽烏魯木齊賽區的H題,看紫書時看到DAG就把這題找了出來,畢竟也是少數自己參加過的比賽。

本題題意:在有n個站點,m條從高到低的滑雪路徑的滑雪場上,找出一條加起來滑的最遠的路徑(只能從高到低劃)。轉化一下就是,給一張有向無環圖(無負權邊),求最長路。

因為是水題,沒有卡空間或時間,也不需要考慮資料溢位,還算良心=_=

ac程式碼:(敲了好久啊——真是菜)

#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
int u[100005],v[100005];
int w[100005],d[10005],r[10005];
int first[10005],next1[100005];
int n,m;
int dp(int i)//i表示第i個點
{
    if(d[i]>0)
        return d[i];
    int k=first[i];//k表示第k條邊
    while(k!=-1)
    {
        d[i]=max(d[i],dp(v[k])+w[k]);
        k=next1[k];
    }
    return d[i];
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int ans=0;
        memset(first,-1,sizeof(first));
        memset(d,0,sizeof(d));
        memset(r,0,sizeof(r));
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&u[i],&v[i],&w[i]);
            next1[i]=first[u[i]];
            first[u[i]]=i;//鄰接表儲存
            r[v[i]]++;//入度
        }
        for(int i=1;i<=n;i++)
        {
            if(r[i]==0)
                ans=max(ans,dp(i));
        }
        printf("%d
",ans);
    }
    return 0;
}

寫這道題順便回顧了一下鄰接表,不過用陣列表示的鄰接表看起來可能沒有指標表示或者用stl來的直觀。

好久不敲程式碼,初始化都沒注意,卡了好久才發現。

順便記一下鄰接表

u,v,w三個陣列分別儲存邊的起始點,指向點和邊的權值。

first陣列儲存每一點的其中一條從此點出發的邊的編號,例如:first[2]=5表示編號為2的點的其中一條邊的編號為5.

next陣列儲存以某一點為出發點的一條邊的下一條邊的編號,例如:next[5]=3表示編號為5的邊的出發點(比如說點1)的下一條邊的編號是3。這樣就可以通過forst和next陣列遍歷每一個點的每一條邊了,且對於稀疏圖而言,佔記憶體遠小於鄰接矩陣。

許多簡單的dp問題可以轉化為DAG問題。紫書上的例子:選擇不同面值的硬幣到達指定面額,可以轉化成求一條從面額s到0的滿足題意的路徑,因為硬幣面額不可能是負的或0,所以這也是一個DAG問題。


相關文章