1. 程式人生 > >POJ3249 工作難題(DAG有向無環圖的單源最短路徑)

POJ3249 工作難題(DAG有向無環圖的單源最短路徑)

題意:

有N個城市,它們之間存在一些單向路徑,若可以從城市i到城市j,則一定無法從城市j到達城市i,只出不入的城市稱為源點城市,只入不出的城市稱為終點城市。已知到達某個城市就可以獲得或者失去一定的錢財。現在狗先生從某個源點出發,到達某個終點停止,他想擁有儘量多的錢財。要求給出最多的錢財是多少。

分析:

題目給定的是一個有向無環圖。最多的錢財可以轉換成最短路徑問題,每條邊的權重其實就是到達該邊的終點得到或失去的錢財。有向無環圖的單源最短路徑可以採用先拓撲排序再按照順序遍歷各個點的邊進行鬆弛的方法進行。由於沒有環,因此肯定存在最短路徑。把最短路徑轉換為最長路徑就可以解決這個問題了,同時這個題目給的是點權,將點權放在他的入度邊上即可,沒有入度的邊再前面加一條即可,即設定一個超級源點,同時將出度是0的點連線到超級匯點,即可。

#include<cstdio>
#include<queue>
#include<cstring>

using namespace std;

const int maxn=1e5+10;
const int maxm=1e6+10;

#define inf 0x3f3f3f3f

struct node{
    int w,to,next;
}g[maxm<<1];

int head[maxn];
int cnt;
int iq;
int dis[maxn],in[maxn],out[maxn],val[maxn],que[maxn];

int n,m;

void add_edge(int u,int v,int w){
    ++in[v];
    ++out[u];
    g[cnt].to=v;
    g[cnt].w=w;
    g[cnt].next=head[u];
    head[u]=cnt++;
}

inline void init(){
    cnt=0;
    memset(head,-1,sizeof head);
    memset(in,0,sizeof in);
    memset(out,0,sizeof out);
}

void toposort(){
    iq=0;
    que[iq++]=0;
    for(int i=0;i<iq;i++){
        for(int k=head[que[i]];k+1;k=g[k].next){
            in[g[k].to]--;
            if(in[g[k].to]==0) que[iq++]=g[k].to;
        }
    }
}

void DAG(){
    for(int i=0;i<=n+1;i++){
        dis[i]=-inf;
    }
    dis[0]=0;
    for(int i=0;i<iq;i++){
        for(int k=head[que[i]];k+1;k=g[k].next){
            if(dis[g[k].to]<dis[que[i]]+g[k].w)
                dis[g[k].to]=dis[que[i]]+g[k].w;
        }
    }
}

int main(){
    while(scanf("%d%d",&n,&m)!=EOF){
        init();
        for(int i=1;i<=n;i++) scanf("%d",&val[i]);
        for(int i=0;i<m;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            add_edge(u,v,val[v]);
        }
        for(int i=1;i<=n;i++){
            if(in[i]==0) add_edge(0,i,val[i]);
            if(out[i]==0) add_edge(i,n+1,0);
        }
        toposort();
        DAG();
        printf("%d\n",dis[n+1]);
    }
    return 0;
}