POJ 3249 拓扑排序+动态规划

该题是让求在一个有向无环图中,从一个入度为 0 的节点出发,到一个出度为 0 的节点的最大的权值问题。我们可以使用广搜,但是会超时,上网查了一下解题报告,可以使用拓扑排序+动态规划来解决此问题:

dp[1] = max{ dp[2] + cost[1] , dp[3] + cost[1] };

dp[2] = cost[2] + dp[4];

dp[3] = cost[3] + dp[4];

dp[4] = cost[4];

dp[5] = cost[5] + dp[6];

dp[6] = cost[6];

在拓扑排序的过程中,是入度为0的节点先入队列,所以我们可以做一个逆置思考,是的 dp[i] 存储的是入度为零的节点到当前节点的最大权值和,而最后在出度为 0 的节点的 dp[i] 中搜索最大的权值。代码实现如下:

#include <iostream>
#include <queue>
#include <cstdio>

using namespace std;
#define N 100100
#define M 1000100
#define INF 1<<29

struct node
{
    int v;
    int next;
} edge[M];

int dp[N];  // 最大价值 存储 由 拓扑排序后 入度为0的节点到当前节点最大的 profit
int enext[N];   //记录节点 i 当前的边,由当前的边 推出 下一条边
int indegree[N];    //入度
int cost[N];    //价值
int res;
int idx;
std::queue<int > q;

int m,n,a,b,i,j;
void input()
{
    for(i=1;i<=n;i++)
    {
        scanf("%d",&cost[i]);
        dp[i] = -INF;
        indegree[i] = 0;
    }

    idx=0;

    //memset(dp,-INF,sizeof(int)*(n+10) );
    //memset(indegree,0,sizeof(int)*(n+10) );    //节点度数初始化为0
    memset(enext,-1,sizeof(enext) ); //说明当前节点只此一条边

    for(i=1;i<=m;i++)
    {
        scanf("%d %d",&a,&b);
        edge[idx].v = b;
        edge[idx].next = enext[a];
        enext[a] = idx++;
        indegree[b]++;
    }
    while( !q.empty() )
        q.pop();
    for( i=1;i<=n;i++)
        if( !indegree[i] )
            q.push(i),dp[i]=cost[i];
}


int dag()
{
    res = -INF;
    while( !q.empty() )
    {
        int cur = q.front();
        q.pop();
        bool flag = true;  //只有节点的出度为 0 的时候,我们才判断其是否有最大profit
        int nextid;
       // cout<<"cur : "<<cur<<endl;
        for( i=enext[cur] ; i != -1 ; i = edge[i].next) //遍历当前顶点的所有的边
        {
            flag = false;
            nextid =  edge[i].v ;

        //    cout<<"nextid : "<<nextid<<endl;
            if( dp[nextid] < cost[ nextid ] + dp[cur])
                dp[nextid] =  cost[nextid] + dp[cur];
            indegree[nextid]--;
            if( !indegree[nextid] )
            {
                q.push(nextid);
            }
        }
        if( flag && dp[cur] > res )
            res = dp[cur];
    }
    return res;
}

int main()
{
    while(scanf("%d %d",&n,&m) != EOF)
    {
        input();
        printf("%d\n",dag());
    }
    return 0;
}

 

说明:本题对时间要求很苛刻,由于数据量大,输入输出必须用 scanf 和 printf 才能保证不超时,同时要避免使用 动态的开辟空间,new 和 delete 同样会有很大的时间开销。

 

 

    原文作者:拓扑排序
    原文地址: https://blog.csdn.net/leo115/article/details/7652118
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞