(板子)缩点 + 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] == ) 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的更多相关文章
- UVA 11324 The Largest Clique(缩点+DAG上的dp)
求最大团.和等价性证明有类似之处,只不过这个不是求互推,而是只要a->b,或b->a即可. 同样的,容易想到先缩点,得到DAG,每个节点上保存SCC的点数,相信任意一条由根节点(入度为零) ...
- BZOJ5017 [Snoi2017]炸弹[线段树优化建边+scc缩点+DAG上DP/线性递推]
方法一: 朴素思路:果断建图,每次二分出一个区间然后要向这个区间每个点连有向边,然后一个环的话是可以互相引爆的,缩点之后就是一个DAG,求每个点出发有多少可达点. 然后注意两个问题: 上述建边显然$n ...
- UVA - 10131Is Bigger Smarter?(DAG上的DP)
题目:UVA - 10131Is Bigger Smarter? (DAG) 题目大意:给出一群大象的体重和IQ.要求挑选最多的大象,组成一个序列.严格的体重递增,IQ递减的序列.输出最多的大象数目和 ...
- P2668 斗地主 dp+深搜版
题目描述 牛牛最近迷上了一种叫斗地主的扑克游戏.斗地主是一种使用黑桃.红心.梅花.方片的A到K加上大小王的共54张牌来进行的扑克牌游戏.在斗地主中,牌的大小关系根据牌的数码表示如下:3<4< ...
- 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 ...
- BZOJ 3998 TJOI2015 弦论 后缀自动机+DAG上的dp
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3998 题意概述:对于一个给定长度为N的字符串,求它的第K小子串是什么,T为0则表示不同位置 ...
- NYOJ16 矩形嵌套 【DAG上的DP/LIS】
矩形嵌套 时间限制:3000 ms | 内存限制:65535 KB 难度:4 描述 有n个矩形,每个矩形可以用a,b来描述,表示长和宽.矩形X(a,b)可以嵌套在矩形Y(c,d)中当且仅当a<c ...
- DAG上的DP
引例:NYOJ16 矩形嵌套 时间限制:3000 ms | 内存限制:65535 KB 难度:4 描述 有n个矩形,每个矩形可以用a,b来描述,表示长和宽.矩形X(a,b)可 ...
- 洛谷 P2392 kkksc03考前临时抱佛脚, dp / 深搜
题目链接 P2392 kkksc03考前临时抱佛脚 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目 dp代码 #include <iostream> #includ ...
随机推荐
- 【NOI Online 2020】入门组 总结&&反思
前言: 这次的NOI Online 2020 入门组我真的无力吐槽CCF的网站了,放段自己写的diss的文章,供一乐 如下:(考试后当天晚上有感而发) 今天是个好日子!!!(我都经历了什么...... ...
- Android APK 重签名
对APK 进行在线 加固后,Apk体积一般会变大,而且Apk会无法直接安装,因为缺少了你的签名.是的,你需要对这个Apk进行重签名. 如何重签名 重签名的方法,一般来说,有两种,第一种是用JDK自带的 ...
- 第三章:Python高级编程-深入类和对象
第三章:Python高级编程-深入类和对象 Python3高级核心技术97讲 笔记 3.1 鸭子类型和多态 """ 当看到一直鸟走起来像鸭子.游泳起来像鸭子.叫起来像鸭子 ...
- C语言设计实验报告(二)
C程序设计实验报告姓 名:赖瑾 实验地点:家 实验时间:2020年3月9日 实验项目:2.3.3 字符与ASCLL码 2.3.4 运算符与表达式的运用 2.3.5 顺序结构应用程序 3.3.1 数学函 ...
- C - A Plug for UNIX POJ - 1087 网络流
You are in charge of setting up the press room for the inaugural meeting of the United Nations Inter ...
- thinkphp操作phpexcel问题
一.thinkphp引入PHPExcel到/Thinkphp/Library/Vendor/ 二.在控制器中引用 public function get_detail() { Vendor(" ...
- x86软路由虚拟化openwrt-koolshare-mod-v2.33联通双拨IPV6教程(第二篇)
续第一篇:https://www.cnblogs.com/zlAurora/p/12433296.html 4 设置多拨 (1)连入OpenWrt Web界面,默认为192.168.1.1,在“网 ...
- C++单例写法
#define __xx(WaveClassFile::me()) class Xx : public QObject{ Q_OBJECT public: static Xx & me(); ...
- python之邮件发送自动化
# -*- coding:utf-8 -*-#@Time : 2020/3/24 22:55#@Autor: Mr.White#@File : 发送邮件.py 一.导入所需要的类 import smt ...
- vue 自己写组件。
最近在网上看到很多大神都有写博客的习惯,坚持写博客不但可以为自己的平时的学习做好记录积累 无意之中也学还能帮助到一些其他的朋友所以今天我也注册一个账号记录一下学习的点滴!当然本人能力实在有限写出的文章 ...