• (板子)缩点 + DAG上的DP(深搜)luogu P3387


    板子传送门

    根据题目意思,我们只需要找出一条点权最大的路径就行了,不限制点的个数。那么考虑对于一个环上的点被选择了,一整条环是不是应该都被选择,这一定很优,能选干嘛不选。很关键的是题目还允许我们重复经过某条边或者某个点,我们就不需要考虑其他了。因此整个环实际上可以看成一个点(选了其中一个点就应该选其他的点)

    拓扑排序

      对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。

    照个人理解,拓扑排序通常是在DAG图中寻找一个适合的解决问题的顺序。

    如何实现拓扑排序

    方法1:BFS(SPFA优化)

    1、先寻找入度为0的点,把它加入队列。

    2、搜寻队列,把队列的点G删去,则如果有点的入度有G点的话,入度- -,当发现又出现入度为0的点时,将该点加入队列。

    3、拓扑排序的结果为该队列,在执行删点操作的时候存储在一个数组及可。

    方法2:记忆化搜索

    大多数情况下,并不需要显式的拓扑排序

    考虑朴素的回溯算法

    若从一个给定的点出发,得到的结果是一样的

    因此对于每个点,计算完成后可以把结果保存起来,之后直接返回查表的结果即可

     

    拓扑排序伪代码(1):

    Topological_sort(G){
        统计图G中每个点的入度(可计算重边,但不可计算自环),记为degree[i]
        初始化queue和result为空的队列,并将所有degree为0的点加入queue
        while (!queue.empty()){
            u = queue.pop() // 队首
            result.push(u)
            for e 是u的出边(若上面计算了重边,这里也要算,与上面一致)
            v是e的指向的点
            degree[v]--
            if (degree[v] == 0) queue.push(v)
        }
        return result
    }

    伪代码(2)

    calculate(u){
        if (u 已经搜索过) return table[u]
        ans = -inf
        for (v 是u的出边指向的点)
        ans = max(ans, value[u] + calculate(v))
        标记u已经搜索过
        table[u] = ans
        return ans
    }
    for (i 是G的所有节点)
    result = max(result, calculate(i))
    print(result)

    为什么要dp

    因为题目说了啊(),其实也很明显啦,对每个点都不断用他的入边的点更新他,取最大值,f[i]表示i点(缩点后)的经过点和最大值。

    方程:

    w代表当前点,rdr数组代表w点的入边的点,dis数组是权值。
    f[w]=max(f[w],f[rdr[w][j-1]]+dis_[w]);

    完整AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define N 100010
    
    inline int read(){
        int x = 0,s = 1;
        char c = getchar();
        while(!isdigit(c)){
            if(c == '-')s = -1;
            c = getchar();
        }
        while(isdigit(c)){
            x = (x << 1) + (x << 3) + (c ^ '0');
            c = getchar();
        }
        return x * s;
    }
    
    struct node{
        int u, v;
        int next;
    } t[N];
    int f[N];
    int dfn[N], scc[N], low[N];
    int stac[N], top = 0; 
    bool vis[N];
    int w[N], sum[N];//单点 + 缩点的值 
    
    int bian = 0;
    inline void add(int u, int v){
        bian++;
        t[bian].u = u;
        t[bian].v = v;
        t[bian].next = f[u];
        f[u] = bian;
        return ;
    }
    
    int cnt = 0, cac = 0;
    void tarjan(int now){
        dfn[now] = low[now] = ++cnt;
        vis[now] = 1;
        stac[++top] = now;
        for(int i = f[now]; i; i = t[i].next){
            int u = t[i].u,v = t[i].v;
            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[now] == low[now]){
            int cur;
            cac++;
            do{
                cur = stac[top--];
                scc[cur] = cac;
                vis[cur] = 0;
                sum[cac] += w[cur];
            }while(cur != now);
        }
        return ;
    } 
    
    int dp[N];
    void search(int now){
        if(dp[now])return;
        dp[now] = sum[now];
        int maxn = 0;
        for(int i = f[now]; i;i = t[i].next){
            int v = t[i].v;
            if(!dp[v])search(v);
            maxn = max(dp[v], maxn);
        }
        dp[now] += maxn;
        return; 
    }
    
    int main(){
        int n = read(), m = read();
        for(int i = 1;i <= n; i++)
            w[i] = read();
        for(int i = 1;i <= m; i++){
            int x = read(), y = read();
             add(x, y);
        } 
        for(int i = 1;i <= n; i++)
            if(!dfn[i]) tarjan(i);
        bian = 0;
        memset(f, 0, sizeof(f));
        for(int i = 1;i <= m; i++){
            t[i].next = 0;
        }
        for(int i = 1;i <= m; i++){
            int u = t[i].u, v = t[i].v;
            if(scc[u] != scc[v]){
                add(scc[u], scc[v]);
            }
        }
        int ans = -(~0u >> 1);
        for(int i = 1;i <= cac; i++){//注意是缩点的个数,这里是新图了 
            if(!dp[i]){
                search(i);//进行记忆化搜索 
                ans = max(ans, dp[i]);
            }
        }
        printf("%d
    ", ans);
        return 0;
    }
  • 相关阅读:
    C# WinForms多线程编程-摇奖程序
    C#多线程编程实例介绍
    C#多线程编程(1) —— 多线程与UI操作
    C#引用类型与值类型浅析
    HTML中空格占位符的几种方式
    C#中字符串排序的问题和解决方法
    InstanceContextMode和ConcurrencyMode的默认值
    The "IsFileSystemCaseSensitive" parameter is not supported by the "FindConfigFiles" task
    jQuery中delegate() 和 on()的出现版本
    NHibernate 分页优化,针对SQLServer(未深入测试)
  • 原文地址:https://www.cnblogs.com/wondering-world/p/12634907.html
Copyright © 2020-2023  润新知