Power OJ 2826 有趣的游戏 【Tanjar缩点+DAG最长路】

传送门

Description 

    一天wangshu迷上了一个十分有趣的游戏,名为sdfsfgdsfsdfsdfse,在在游戏里你需要从一个起始补给点开始去寻找装备,每一个补给点都有一定价值的装备,最后从出口补给点出去,这个游戏十分有趣,所以当你离开一个补给点后这个补给点会重生和原来一样价值的装备,当你达到一个补给点后就会立即得到当前补给点价值的奖励,然后去下一个补给点,当然从一个补给点到下一个补给点的路径是单向的,你只能沿着这条路过去不能沿着这条路回来,当然你可能会得到无限多的奖励,到了出口也可以先不出去,继续去下一个补给点,同样也可能无法到达出口补给点,无法到达出口点当然是毫无意义的。

Input 

第一行两个数n,m,分别表示有n个补给点,m条路,其中编号为1的补给点为起点,编号为n的补给点为出口点。 第二行包括n个数依次表示1到n号补给点的可得的奖励值ai, 接下来m行每行两个数,st,en表示可以从补给点st到补给点en。 保证不会出现重边和自环。

Output 

如果可以得到无穷大的奖励并能出去输出"Infinitely",如果不能到达出口则输出"Unreachable",否则输出能得到的最大奖励。

思路:这道题给的边为单项边,由题意能知道可能会获得无穷大奖励,则说明图中会有环,就能够想到Tanjar缩点,将强连通分量的权值赋值为极大值(单独点任为原来的权值),我们会得到DAG图,我们判断起点能否到达终点,如果可以到达终点能获得的最大奖励是多少,我们用到DAG最长路解决(固定终点的最长路)。

求DAG固定终点最长路:设固定终点为e,dp[MAX_N],dp[i]表示从 i 顶点到固定终点 e 能够获得的最大长度。
那么我们可以得到递推方程 dp[ i ]=max( dp[ i ],dp[ j ] +length(i,j) )。
dp数组的初始化问题:因为固定了终点,因此边界应当为dp[T]=0。由于从某顶点出发可能无法到达终点T(例如出度为0的顶点),合适的做法是初始化dp数组为一个负的大数,来保证“”无法到达终点”的含义得以表达(即-Inf,消除其他出度为0的顶点对前驱结点的最长距离的干扰)。

DAG最长路参考博客:https://blog.csdn.net/jiangpeng59/article/details/56666903

坑点:①:起点和终点是同一点(即n=1时) ②:Tanjar缩点后,起点和终点为同一个点了,答案应该为:Infinitely 。

附上代码:

///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<bitset>
#include<set>
#include<stack>
#include<map>
#include<list>
#include<new>
#include<vector>
#define MT(a,b) memset(a,b,sizeof(a));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai=acos(-1.0);
const double E=2.718281828459;
const ll mod=1e12;
const int INF=0x3f3f3f3f;

ll n,m,value[5005];
ll sum[5005];
ll dp[5005];

struct node
{
    int e;
    ll p;
}load[10005];
int head[5005],sign;
vector<int>edge[5005];

void add_edge(int s,int e)
{
    load[++sign]=node{e,head[s]};
    head[s]=sign;
}

int dfn[5005],low[5005],times;
int stack_[5005],top;
bool instack[5005];
int belong[5005],cnt;
int c[5005];

void tanjar(int s)
{
    dfn[s]=low[s]=++times;
    instack[s]=1;
    stack_[++top]=s;
    for(int i=head[s];i!=-1;i=load[i].p)
    {
        int e=load[i].e;
        if(!dfn[e])
        {
            tanjar(e);
            low[s]=min(low[s],low[e]);
        }
        else
        {
            if(instack[e])
                low[s]=min(low[s],dfn[e]);
        }
    }
    int t;
    if(dfn[s]==low[s])
    {
        cnt++;
        do
        {
            t=stack_[top--];
            instack[t]=0;
            belong[t]=cnt;
            c[cnt]++;
        }while(s!=t);
    }
}

ll dfs(int s)
{
    if(dp[s]!=-5005*mod)
        return dp[s];
    int d=edge[s].size();
    for(int i=0;i<d;i++)
    {
        int e=edge[s][i];
        dp[s]=max(dp[s],dfs(e)+sum[e]);
    }
    return dp[s];
}

void init()
{
    sign=times=top=cnt=0;
    for(int i=0;i<=n;i++)
    {
        head[i]=-1;
        dfn[i]=low[i]=0;
        sum[i]=0;
        c[i]=0;
        dp[i]=-5005*mod;
    }
}

int main()
{
    int s,e;
    scanf("%lld %lld",&n,&m);
    init();
    for(int i=1;i<=n;i++)
        scanf("%lld",&value[i]);
    for(int i=1;i<=m;i++)
    {
        scanf("%d %d",&s,&e);
        add_edge(s,e);
    }
    if(n==1)
    {
        printf("%lld\n",value[1]);
        return 0;
    }
    for(int i=1;i<=n;i++)
    {
        if(!dfn[i])
            tanjar(i);
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=head[i];j!=-1;j=load[j].p)
        {
            e=load[j].e;
            if(belong[i]!=belong[e])
                edge[belong[i]].push_back(belong[e]);
        }
        if(c[belong[i]]==1)
            sum[belong[i]]=value[i];
        else
            sum[belong[i]]=mod;
    }
    if(belong[1]==belong[n])
    {
        printf("Infinitely\n");
        return 0;
    }
    dp[belong[n]]=0;
    dfs(belong[1]);
    if(dp[belong[1]]<=0)
        printf("Unreachable\n");
    else if(dp[belong[1]]+sum[belong[1]]>=mod)
        printf("Infinitely\n");
    else
        printf("%lld\n",dp[belong[1]]+sum[belong[1]]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42211531/article/details/87933080