板子传送门

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

拓扑排序

对一个有向无环图(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] == ) 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-]]+dis_[w]);

完整AC代码:

#include<bits/stdc++.h>
using namespace std;
#define N 100010 inline int read(){
int x = ,s = ;
char c = getchar();
while(!isdigit(c)){
if(c == '-')s = -;
c = getchar();
}
while(isdigit(c)){
x = (x << ) + (x << ) + (c ^ '');
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 = ;
bool vis[N];
int w[N], sum[N];//单点 + 缩点的值 int bian = ;
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 = , cac = ;
void tarjan(int now){
dfn[now] = low[now] = ++cnt;
vis[now] = ;
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] = ;
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 = ;
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 = ;i <= n; i++)
w[i] = read();
for(int i = ;i <= m; i++){
int x = read(), y = read();
add(x, y);
}
for(int i = ;i <= n; i++)
if(!dfn[i]) tarjan(i);
bian = ;
memset(f, , sizeof(f));
for(int i = ;i <= m; i++){
t[i].next = ;
}
for(int i = ;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 >> );
for(int i = ;i <= cac; i++){//注意是缩点的个数,这里是新图了
if(!dp[i]){
search(i);//进行记忆化搜索
ans = max(ans, dp[i]);
}
}
printf("%d\n", ans);
return ;
}

(板子)缩点 + DAG上的DP(深搜)luogu P3387的更多相关文章

  1. UVA 11324 The Largest Clique(缩点+DAG上的dp)

    求最大团.和等价性证明有类似之处,只不过这个不是求互推,而是只要a->b,或b->a即可. 同样的,容易想到先缩点,得到DAG,每个节点上保存SCC的点数,相信任意一条由根节点(入度为零) ...

  2. BZOJ5017 [Snoi2017]炸弹[线段树优化建边+scc缩点+DAG上DP/线性递推]

    方法一: 朴素思路:果断建图,每次二分出一个区间然后要向这个区间每个点连有向边,然后一个环的话是可以互相引爆的,缩点之后就是一个DAG,求每个点出发有多少可达点. 然后注意两个问题: 上述建边显然$n ...

  3. UVA - 10131Is Bigger Smarter?(DAG上的DP)

    题目:UVA - 10131Is Bigger Smarter? (DAG) 题目大意:给出一群大象的体重和IQ.要求挑选最多的大象,组成一个序列.严格的体重递增,IQ递减的序列.输出最多的大象数目和 ...

  4. P2668 斗地主 dp+深搜版

    题目描述 牛牛最近迷上了一种叫斗地主的扑克游戏.斗地主是一种使用黑桃.红心.梅花.方片的A到K加上大小王的共54张牌来进行的扑克牌游戏.在斗地主中,牌的大小关系根据牌的数码表示如下:3<4< ...

  5. bzoj1093: [ZJOI2007]最大半连通子图 scc缩点+dag上dp

    一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:?u,v∈V,满足u→v或v→u,即对于图中任意两点u,v,存在一条u到v的有向路径或者从v到u的有向路径.若G'=(V ...

  6. BZOJ 3998 TJOI2015 弦论 后缀自动机+DAG上的dp

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3998 题意概述:对于一个给定长度为N的字符串,求它的第K小子串是什么,T为0则表示不同位置 ...

  7. NYOJ16 矩形嵌套 【DAG上的DP/LIS】

    矩形嵌套 时间限制:3000 ms | 内存限制:65535 KB 难度:4 描述 有n个矩形,每个矩形可以用a,b来描述,表示长和宽.矩形X(a,b)可以嵌套在矩形Y(c,d)中当且仅当a<c ...

  8. DAG上的DP

    引例:NYOJ16 矩形嵌套 时间限制:3000 ms  |           内存限制:65535 KB 难度:4   描述 有n个矩形,每个矩形可以用a,b来描述,表示长和宽.矩形X(a,b)可 ...

  9. 洛谷 P2392 kkksc03考前临时抱佛脚, dp / 深搜

    题目链接 P2392 kkksc03考前临时抱佛脚 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目 dp代码 #include <iostream> #includ ...

随机推荐

  1. 数学--数论--HDU1792A New Change Problem(GCD规律推导)

    A New Change Problem Problem Description Now given two kinds of coins A and B,which satisfy that GCD ...

  2. 自定义Element父子不关联的穿梭树

    Element自身是有一个Transfer穿梭框组件的,这个组件是穿梭框结合checkbox复选框来实现的,功能比较单一,自己想实现这个功能也是很简单的,只是在项目开发中,项目排期紧,没有闲功夫来实现 ...

  3. Java笔记(day20-22)

    IO流: 输入流.输出流 字节流.字符流:为了处理文字数据方便而出现的对象. (其实这些对象的内部使用的还是字节流(因为文字最终也是字节数据,只不过,通过字节流读取了相对应的字节数,没有对这些字节直接 ...

  4. 面试之JS深拷贝的实现

    在面试中你是否遇到过如下场景: Q:小朋友,你是否了解如何拷贝一个对象? R:此时,机智的你可能会想到 Object.assign({}, obj); Q:那如何深拷贝一个对象呢? R:机智的你 JS ...

  5. springboot中json转换LocalDateTime失败的bug解决过程

    环境:jdk1.8.maven.springboot 问题:前端通过json传了一个日期:date:2019-03-01(我限制不了前端开发给到后端的日期为固定格式,有些人就是这么不配合),      ...

  6. Python 为什么抛弃累赘的花括号,使用缩进来划分代码块?

    大家好,这是"Python为什么"系列节目的文字稿(文末有观看地址). 本期话题:Python 为什么使用缩进来划分代码块,而不像其它语言使用花括号 {} 或者 "end ...

  7. Redis学习笔记(六) 对象

    前面我们看了Redis用到的主要数据结构,如简单动态字符串(SDS).双向链表.字典.压缩列表.整数集合等. 但是Redis并没有直接使用这些数据结构来实现键值对,而是基于这些数据结构创建了一个对象系 ...

  8. TC SRM498 Div1 1000PT(容斥原理+DP)

    [\(Description\)] 网格中每步可以走\((0,\cdots M_x,0\cdots M_y)\)中任意非零向量,有\(K\)种向量不能走,分别是\((r_1,r_1),(r_2,r_2 ...

  9. go 函数 方法 接口

    概论 函数 方法 接口 概论 方法在编译时静态绑定,依托于具体的类型 接口对应的方法是在运行时动态绑定 进程内初始化顺序 初始化导入包的常量和变量(可以导出的变量)--->包的init函数,不同 ...

  10. Spring全家桶之spring boot(四)

    spring boot拦截器.过滤器.servlet和健康检查机制  spring boot拦截器 spring boot配置拦截器与原来大致相同,只是需要在拦截器的配置类上添加@Configurat ...