洛谷 题解 P3627 【[APIO2009]抢掠计划】

图论 tarjan缩点+最短路 的一道题

  • tarjan求强连通分量(为以后缩点打下良好的基础)

(如果不会tarjan的请点击这儿)

你需要的东西:
(1)、dfn[],表示这个点在dfs时是第几个被搜到的。
(2)、low[],表示这个点以及其子孙节点连的所有点中dfn最小的值
(3)、stack[],表示当前所有可能能构成是强连通分量的点。
(4)、vis[],表示一个点是否在stack中。
(5)、color[],记录每一个点强连通分量的编号。
(6)、deep,记录dfs树的深度
inline void tarjan(int now)
{
    dfn[now]=++deep;
    low[now]=deep;
    vis[now]=1;
    st.push(now);
    for(int i=0;i<ver[now].size();i++)
    {
        int x=ver[now][i];
        if(!dfn[x])
        {
            tarjan(x);
            low[now]=min(low[now],low[x]);
        }
        else
        {
            if(vis[x])
                low[now]=min(low[now],low[x]);
        }
    }
    if(dfn[now]==low[now])
    {
        color[now]=++sum;
        vis[now]=0;
        while(st.top()!=now)
        {
            color[st.top()]=sum;
            vis[st.top()]=0;
            st.pop();
        }
        st.pop();
    }
}
  • 缩点(去除图中的环)
//重点:这里建新图是依托强连通分量的编号来建的
for(int i=1;i<=n;i++)
    {
        for(int j=0;j<ver[i].size();j++)
        {
            int x=ver[i][j];
            if(color[i]!=color[x])//如果不是属于同一个强连通分量中,那么就合并
            {
                g[color[i]].push_back(color[x]);
           //千万不能写成g[i].push_back(x);坑死我了
            }
        }
    }
//这一部分代码还可以适当优化...(想一想)
  • 对于点权与酒馆的一些处理(方便求最短路)
for(int i=1;i<=n;i++)
    {
        ww[color[i]]+=w[i];//将这个强连通分量中所有的点权全部加起来
        if(tf[i])tf[color[i]]=1;//只要这个强连通分量中有一个结点有酒馆,那么就设定为有酒馆
    }
  • 求最短路模板(然而实际是最长路)
//模板不做解释
inline void spfa()
{
    d[color[s]]=ww[color[s]];
    queue<int>q;
    q.push(color[s]);
    while(q.size())
    {
        int now=q.front();
        q.pop();
        for(int i=0;i<g[now].size();i++)
        {
            int x=g[now][i];
            if(d[now]+ww[x]>d[x])
            {
                d[x]=d[now]+ww[x];
                q.push(x);
            }
        }
    }
}

所以...

在所有有酒馆的节点中选一个最大值输出就好了

for(int i=1;i<=sum;i++)
    {
        //cout<<d[i]<<" ";
        if(tf[i])ans=max(ans,d[i]);
    }

完整代码

#include<bits/stdc++.h>
using namespace std;
const int MAXN=500000+10;
int n,m;
vector<int>ver[MAXN];
vector<int>g[MAXN];
int w[MAXN],ww[MAXN];
bool tf[MAXN];
int s,p,ans=0;
int dfn[MAXN],color[MAXN],low[MAXN];
int deep,sum;
bool vis[MAXN];
int d[MAXN];
stack<int>st;
inline int read()
{
    int tot=0;
    char c=getchar();
    while(c<'0'||c>'9')
        c=getchar();
    while(c>='0'&&c<='9')
    {
        tot=tot*10+c-'0';
        c=getchar();
    }
    return tot;
}
inline void tarjan(int now)
{
    dfn[now]=++deep;
    low[now]=deep;
    vis[now]=1;
    st.push(now);
    for(int i=0;i<ver[now].size();i++)
    {
        int x=ver[now][i];
        if(!dfn[x])
        {
            tarjan(x);
            low[now]=min(low[now],low[x]);
        }
        else
        {
            if(vis[x])
                low[now]=min(low[now],low[x]);
        }
    }
    if(dfn[now]==low[now])
    {
        color[now]=++sum;
        vis[now]=0;
        while(st.top()!=now)
        {
            color[st.top()]=sum;
            vis[st.top()]=0;
            st.pop();
        }
        st.pop();
    }
}
inline void spfa()
{
    d[color[s]]=ww[color[s]];
    queue<int>q;
    q.push(color[s]);
    while(q.size())
    {
        int now=q.front();
        q.pop();
        for(int i=0;i<g[now].size();i++)
        {
            int x=g[now][i];
            if(d[now]+ww[x]>d[x])
            {
                d[x]=d[now]+ww[x];
                q.push(x);
            }
        }
    }
}
int main()
{
    //freopen("testdata.in","r",stdin);
    n=read();m=read();
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        ver[x].push_back(y);
    }
    for(int i=1;i<=n;i++)
        w[i]=read();
    s=read();p=read();
    for(int i=1;i<=p;i++)
    {
        int x=read();
        tf[x]=1;
    }
    for(int i=1;i<=n;i++)
    {
        if(!dfn[i])
            tarjan(i);
    }
    /*cout<<endl;
    for(int i=1;i<=n;i++)
        cout<<color[i]<<" ";
    cout<<endl;*/
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<ver[i].size();j++)
        {
            int x=ver[i][j];
            if(color[i]!=color[x])
            {
                //cout<<i<<" "<<x<<" "<<color[i]<<" "<<color[x]<<endl;
                g[color[i]].push_back(color[x]);
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        ww[color[i]]+=w[i];
        if(tf[i])tf[color[i]]=1;
    }
    /*cout<<color[s]<<endl;
    cout<<endl;
    for(int i=1;i<=sum;i++)
    {
        for(int j=0;j<g[i].size();j++)cout<<i<<" "<<g[i][j]<<"\n";
    }
    cout<<endl;
    for(int i=1;i<=sum;i++)
        cout<<ww[i]<<" ";cout<<endl;
    for(int i=1;i<=sum;i++)
        cout<<tf[i]<<" ";cout<<endl;
    cout<<ww[color[s]]<<endl;*/
    spfa();
    for(int i=1;i<=sum;i++)
    {
        //cout<<d[i]<<" ";
        if(tf[i])ans=max(ans,d[i]);
    }
    //cout<<endl;
    cout<<ans<<endl;
    return 0;
}

转载于:https://www.cnblogs.com/hulean/p/11119915.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值