【题解】洛谷P3627 [APIO2009]抢掠计划(缩点+SPFA)

洛谷P3627:https://www.luogu.org/problemnew/show/P3627

思路

由于有强连通分量 所以我们可以想到先把整个图缩点

缩点完之后再建一次图 把点权改为边权 并把边权转为负数 即可用SPFA求最短路间接求最长路了

最后我们查询所有的酒吧 跳出最大的ans即可

思路简单 但是代码有些冗长

代码

#include<iostream>
#include<cstring>
using namespace std;
#define maxn 500010
int n,m,s,p,top,num,cnt,col,ans,t,w;
int h[maxn],de[maxn],st[maxn],dfn[maxn],low[maxn],co[maxn],money[maxn],sum[maxn],q[maxn],dis[maxn],x[maxn],y[maxn];
bool vis[maxn],exist[maxn];
struct Edge
{
    int to;
    int next;
    int w;
}e[maxn];
void add(int u,int v)//Tarjan的边 
{
    e[++cnt].to=v;
    e[cnt].next=h[u];
    h[u]=cnt;
}
void read()//输入 
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        cin>>x[i]>>y[i];
        add(x[i],y[i]);
    }
    for(int i=1;i<=n;i++)
    cin>>money[i];
    cin>>s;
}
void Add(int u,int v,int w)//SPFA的边 
{
    e[++cnt].w=w;
    e[cnt].to=v;
    e[cnt].next=h[u];
    h[u]=cnt;
}
void Tarjan(int u)//标准Tarjan 
{
    dfn[u]=low[u]=++num;
    vis[u]=1;
    st[++top]=u;
    for(int i=h[u];i;i=e[i].next)
    {
        int v=e[i].to;
        if(!dfn[v])
        {
            Tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else
        if(vis[v])
        {
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(dfn[u]==low[u])
    {
        col++;
        while(st[top+1]!=u)
        {
            sum[col]+=money[st[top]];//计算此强连通分量的总价值 
            co[st[top]]=col;
            vis[st[top--]]=0;
        }
    }
}
void SPFA()//标准SPFA 
{
    memset(dis,127,sizeof(dis));
    dis[co[s]]=-sum[co[s]];//负权 
    q[1]=co[s];//把市中心所在的点入队 
    t=0;
    w=1;
    while(t<w)
    {
        t++;
        int u=q[t];
        exist[u]=0;
        for(int i=h[u];i;i=e[i].next)
        {
            int v=e[i].to;
            if(dis[v]>dis[u]+e[i].w)
            {
                dis[v]=dis[u]+e[i].w;
                if(!exist[v])
                {
                    w++;
                    q[w]=v;
                    exist[v]=1;            
                }
            }
        }
    }
}
int main()
{
    read();
    for(int i=1;i<=n;i++)
    if(!dfn[i]) Tarjan(i);
    cnt=0;
    memset(e,0,sizeof(e));
    memset(h,0,sizeof(h));//注意清空边 
    for(int i=1;i<=m;i++)
        if(co[x[i]]!=co[y[i]])
        Add(co[x[i]],co[y[i]],-sum[co[y[i]]]);//负权边 
    SPFA();
    cin>>p;
    for(int i=1;i<=p;i++)//枚举所有酒吧求最大值 
    {
        int bar;
        cin>>bar;
        if(-dis[co[bar]]>ans)
        ans=-dis[co[bar]];
    }
    cout<<ans;
}
View Code

 

posted @ 2018-09-26 21:46  Nanchtiy  阅读(126)  评论(0编辑  收藏  举报