抢掠计划

https://loj.ac/problem/10096

题目描述

  某人准备从1号节点出发开始抢劫,并在一个有酒吧的节点停止抢劫,已知每个节点ATM机拥有的钱数,求最多抢劫的钱数(可重复经过道路,抢完后ATM机没钱)。

思路

  我们考虑对于一个强连通分量,他必定可以抢完这个强连通分量中所有的钱并到达任意节点,因此我们可以缩点。缩点之后每个点的点权即为这个强连通分量的总金额。我们对于缩点后的DAG,显然可以将点权转化为边权,在这张图上找最长路。这里最长路我们可以用spfa实现,这里我们避免用拓扑,因为用拓扑排序时我们只能先加入起点,而不能加入其它度为0的点,否则会到达无法到达的点,在实现上比较复杂,而直接最短路实际并不比拓扑慢多少。

代码

#include <bits/stdc++.h>
using namespace std;
const int N=5e5+10,M=5e5+10;

struct Edge
{
    int x,y;
}e[M];

int v[N];
int nxt[M],to[M],head[N],w[N],tot;
void add_edge(int x,int y,int v)
{
    nxt[++tot]=head[x];
    head[x]=tot;
    to[tot]=y;
    w[tot]=v;
}

int read()
{
    int res=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){res=(res<<3)+(res<<1)+ch-'0';ch=getchar();}
    return res*w;
}

int dfn[N],low[N],st[N],co[N],sum[N],top,col,idx;
void tarjan(int u)
{
    dfn[u]=low[u]=++idx;
    st[++top]=u;
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(!co[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u])
    {
        co[u]=++col;
        sum[col]=v[u];
        while(st[top]!=u)
        {
            co[st[top]]=col;
            sum[col]+=v[st[top]];
            --top;
        }
        --top;
    }
}

int m;
void rebuild()
{
    tot=0;
    memset(head,0,sizeof(head));
    for(int i=1;i<=m;i++)
    {
        int u=co[e[i].x],v=co[e[i].y];
        if(u!=v)
            add_edge(u,v,sum[v]);
    }
}

int dis[N];
bool exist[N];
void spfa(int s)
{
    queue<int>q;
    dis[s]=sum[s];exist[s]=1;
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        exist[u]=0;
        for(int i=head[u];i;i=nxt[i])
        {
            int v=to[i];
            if(dis[v]<dis[u]+w[i])
            {
                dis[v]=dis[u]+w[i];
                if(!exist[v])
                {
                    exist[v]=1;
                    q.push(v);
                }
            }
        }
    }
}
int ed[N];
int main() 
{
    int n,s,p;
    n=read();m=read();
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        add_edge(x,y,0);
        e[i].x=x;e[i].y=y;
    }
    for(int i=1;i<=n;i++)
        v[i]=read();
    s=read();p=read();
    for(int i=1;i<=p;i++)
        ed[i]=read();
    for(int i=1;i<=n;i++)
        if(!dfn[i])tarjan(i);
    rebuild();
    spfa(co[s]);
    int ans=0;
    for(int i=1;i<=p;i++)
        ans=max(ans,dis[co[ed[i]]]);
    printf("%d",ans);
}

 

posted @ 2019-10-23 21:04  fbz  阅读(157)  评论(0编辑  收藏  举报