Loading

抢掠计划

算法

先转化题意

在有 \(n\) 个点 \(m\) 条边的有向图上, 从点 \(S\) 开始, 最终到达一个拥有酒吧的点
求途径最大点权和, 其中可以重复经过一个点, 但是点权和不再计算

现在动用一下注意力, 在抽象有向图上不好处理, 我们考虑 \(\rm{DAG}\) 的情况

显然的, 对于一个 \(\rm{DAG}\) , 问题变得简单, 只需要按拓扑序跑 \(\rm{dp}\) 即可

那么考虑将原图缩点成为 \(\rm{DAG}\) 之后怎么处理

显然, 我们直接记录缩点之后的边权和即可

看题解发现 \(\rm{spfa}\) 跑最长路也是可以的

代码

当做强连通分量的板子练一下

#include <bits/stdc++.h>
const int MAXN = 5e5 + 20;
const int MAXM = 5e5 + 20;

int N, M;

int S, P;

class Graph_Class
{
private:

public:
    struct node
    {
        int to, w;
        int next;
    } Edge[MAXM];
    int Val[MAXN];
    int Edge_Cnt = 0;
    int head[MAXN];
    bool End[MAXN]; // 是否为酒吧
    int InDegree[MAXN];
    void head_init() { for (int i = 1; i <= N; i++) head[i] = -1; }

    void addedge(int u, int v) {
        Edge[++Edge_Cnt].to = v;
        Edge[Edge_Cnt].next = head[u];
        head[u] = Edge_Cnt;
    }
};
Graph_Class Graph1; // 原图
Graph_Class Graph2; // 缩点图
bool InGraph2[MAXN]; // 记录每个原图中的点是否在缩点图中

class Tarjan_Class
{
private:
    int low[MAXN], dfn[MAXN], num = 0;
    int sccno[MAXN];
    std::stack<int> Scc; // 辅助栈
    void dfs1(int Now)
    {
        Scc.push(Now);
        low[Now] = dfn[Now] = ++num;
        for (int i = Graph1.head[Now]; ~i; i = Graph1.Edge[i].next) {
            int NowTo = Graph1.Edge[i].to, NowW = Graph1.Edge[i].w;
            /*下降边*/
            if (!dfn[NowTo]) {
                dfs1(NowTo);
                low[Now] = std::min(low[Now], low[NowTo]);
            }
            /*上升边*/
            else if (!sccno[NowTo]){
                low[Now] = std::min(low[Now], dfn[NowTo]);
            }
        }
        if (low[Now] == dfn[Now]) {
            scc_cnt++;
            Graph2.Val[scc_cnt] = 0;
            Graph2.End[scc_cnt] = false;
            while (!Scc.empty()) {
                int NowSCC = Scc.top();
                Scc.pop();
                sccno[NowSCC] = scc_cnt;
                Graph2.Val[scc_cnt] += Graph1.Val[NowSCC];
                Graph2.End[scc_cnt] |= Graph1.End[NowSCC];
                if (Now == NowSCC) break;
            }
        }
    }

public:
    int scc_cnt = 0;
    /*tarjan 求缩点图*/
    void solve()
    {
        dfs1(S); // 从市中心开始缩点
        
        Graph2.head_init();
        for (int i = 1; i <= N; i++) {
            /*标记无意义的点 (市中心出发无法到达)*/
            if (dfn[i]) InGraph2[i] = true;
            else continue;

            int scc_i = sccno[i];
            for (int j = Graph1.head[i]; ~j; j = Graph1.Edge[j].next) {
                int point_j = Graph1.Edge[j].to;
                if (!dfn[point_j]) continue;
                int scc_j = sccno[point_j];
                if (scc_j == scc_i) continue;

                Graph2.addedge(scc_i, scc_j);
                Graph2.InDegree[scc_j]++;
            }
        }
    }
} Tarjan;

class Sol_Class
{
protected:
    std::queue<int> Q;
    int LSY[MAXN], LSY_cnt = 0;
    /*topo 排序*/
    void topo()
    {
        for (int i = 1; i <= Tarjan.scc_cnt; i++) {
            if (!Graph2.InDegree[i]) Q.push(i);
        }
        while (!Q.empty())
        {
            int Now = Q.front();
            LSY[++LSY_cnt] = Now;
            Q.pop();
            for (int i = Graph2.head[Now]; ~i; i = Graph2.Edge[i].next) {
                int NowTo = Graph2.Edge[i].to, NowW = Graph2.Edge[i].w;
                if (!(--Graph2.InDegree[NowTo])) {
                    Q.push(NowTo);
                }
            }
        }
    }

    int dp[MAXN];

public:
    /*dp 求最长路*/
    void solve()
    {
        topo();
        int Ans = 0;
        memset(dp, 0, sizeof(dp));
        dp[LSY[1]] = Graph2.Val[LSY[1]];
        for (int i = 1; i <= LSY_cnt; i++)
        {
            int Now = LSY[i];
            for (int j = Graph2.head[Now]; ~j; j = Graph2.Edge[j].next) {
                int NowTo = Graph2.Edge[j].to;
                dp[NowTo] = std::max(dp[NowTo], dp[Now] + Graph2.Val[NowTo]);
            }

            if (Graph2.End[Now]) Ans = std::max(Ans, dp[Now]);
        }

        printf("%d", Ans);
    }
} Sol;

int main()
{
    scanf("%d %d", &N, &M);
    Graph1.head_init();
    for (int i = 1; i <= M; i++) {
        int u, v;
        scanf("%d %d", &u, &v);
        Graph1.addedge(u, v);
    }

    for (int i = 1; i <= N; i++)
        scanf("%d", &Graph1.Val[i]);
    scanf("%d %d", &S, &P);
    for (int i = 1; i <= P; i++) {
        int u;
        scanf("%d", &u);
        Graph1.End[u] = true; // 标记酒吧
    }

    Tarjan.solve();

    Sol.solve();

    return 0;
}

总结

简单题

posted @ 2024-11-22 20:49  Yorg  阅读(26)  评论(0)    收藏  举报