P3387缩点(tarjan+拓扑排序+线性dp)
题目描述
给定一个 n个点 m 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。
输入格式
第一行两个正整数 n,,m
第二行 n 个整数,依次代表点权
第三至 m+2行,每行两个整数 u,v,表示一条 u→v 的有向边。
输出格式
共一行,最大的点权之和。
题目链接:P3387
分析:相信大家已经看过题目了,在看我的题解之前也应该看过别的大佬的题解了(毕竟我是蒟蒻),虽然所有的题解一上来就会明确告诉你思路是tarjan+拓扑+dp,但为什么是这个思路确很少分析,我们不妨来分析一下加深自己的理解
对于这道题,要找出加权有向图中一条路径上最大的权值和sum(w),w我们第一时间想到是什么情况下sum(w)最大,第一反应是路径越长则sum(w)趋于越大,虽然极其的不严格,但却好比一根团绳子(一个图),表面上很难看出来哪一根最长,因为有折叠起来的部分(环),所以第一步要拆折叠为直线(tarjan缩点),在缩点后,有向图中的环消失,原图中的环被缩成了点.
好,现在折叠的部分被拆开了,图现在变成了有向无环图,路径清晰明确,是时候进行dp了,用dp来找出最优解(类似于最长上升子序列,在选与不选中抉择),但是想要进行dp,还要满足以下两个条件:
1.最优子结构.
2.无后效性.
对于最优子结构:我们规定,dp[i]为当dp到点i时的最优情况,那么dp[n]为最终的唯一解,设最后一次选取在点u->n之间,那么,要是dp[n]最大,有dp[n]=max(dp[n],dp[u]+w[n]);
证明:假设dp[u]不符合最优解,那么则必定存在有dp1[u]>dp[u],那么有dp1[n]=dp1[u]+w[v]>dp[u],这与dp[n]的最优性矛盾,因为在n时的dp[n]已经是唯一最优解.
然而对于无后效性,在某次dp时因为dp此时的图并不是严格线性的,合并前的状态有可能影响合并后的走向,比如遇到多出度的点,无法保证无后效性
那么这时就要用到拓扑排序,我个人的理解为,将一个有向无环图变为一个线性序列,在这个线性序列中,取任意两点u,v,如果从u->v出现在缩点后图的传递闭包中,那么在队列中u必然在v之前.
这时,dp变成了线性dp,无后效性轻松保证.
具体的算法实现
好,现在我们知道为什么要用这三样东西了,思路就变得清晰起来.首先对于tarjan,如果是从受欢迎的牛或者从其他强连通分量的题过来的小伙伴应该大致有一些了解.
在tarjan的过程中主要是动态的更新两个东西,dfn和low,代表着按时间上遍历到的顺序情况和够追溯到的最早的栈中节点的次序号,当dfn[u]==low[u]时,即某一次dfn时又指回了low[u],所以可以成环.
这里安利一下我的另一篇题解:P1262间谍网络
对于拓扑排序我一般写bfs(其他方法记不住,因为比较菜),具体思路为,找入度为0的点加入队列,然后删除该点的所有出度边,在次寻找,重复下去直至空图,类似于dijkstra,只不过这里找的是路径最大权值和.
那么废话就不多说了,上码
#include<bits/stdc++.h>
#define N 100005
using namespace std;
struct node
{
int next,to;
} e[N*];//原图
struct node1
{
int to,next;
}e1[N*];//缩点后的图
int head[*N],dfn[N*],low[N*];
int vis[N*],color[N*],sd[N*];
int w[*N],dp[N*];
int head1[N*];
int tim=,total,n,m,cnt,cnt1;
stack<int> q;
inline void add(int x,int y)
{
cnt++;
e[cnt].to=y;
e[cnt].next=head[x];
head[x]=cnt;
}
inline void add1(int x,int y)
{
cnt1++;
e1[cnt1].next=head1[x];
e1[cnt1].to=y;
head1[x]=cnt1;
}
void tarjan(int x)
{
dfn[x]=low[x]=tim;
tim++;
vis[x]=true;
q.push(x);
for(int i=head[x];i;i=e[i].next)
{
int u=e[i].to;
if(!dfn[u])
{
tarjan(u);
low[x]=min(low[x],low[u]);
}
else if(vis[u]) low[x]=min(low[x],dfn[u]);
}
if(low[x]==dfn[x])
{ total++;
int y;
do{
y=q.top();
q.pop();
vis[y]=false;
color[y]=total;
sd[total]+=w[y];
}while(x!=y);
}
}
int dijkstra(int x)
{
int sum=;
memset(dp,-0x3f,sizeof(dp));
memset(vis,false,sizeof(vis));
queue<int> Q;
Q.push(x);
vis[x]=true;
dp[x]=;
while(!Q.empty())
{
x=Q.front();
Q.pop();
vis[x]=false;
sum=max(sum,dp[x]+sd[x]);
for(int i=head1[x];i;i=e1[i].next)
{
int y=e1[i].to;
if(dp[y]<dp[x]+sd[x])
dp[y]=dp[x]+sd[x];
if(vis[y]) continue;
Q.push(y);
}
}
return sum;
}
int main()
{
cin>>n>>m;
for(int i=;i<=n;i++)
{
cin>>w[i];
}
for(int i=;i<=m;i++)
{
int x,y;
cin>>x>>y;
add(x,y);
}
for(int i=;i<=n;i++)
{
if(!dfn[i])
tarjan(i);
}
for(int i=;i<=n;i++)
for(int j=head[i];j;j=e[j].next)
{
int v=e[j].to;
if(color[i]!=color[v])
{
add1(color[i],color[v]);
}
}
int ans=;
for(int i=;i<=total;i++)
{
ans=max(dijkstra(i),ans);
}
cout<<ans;
return ;
}
P3387缩点(tarjan+拓扑排序+线性dp)的更多相关文章
- 【bzoj1093】[ZJOI2007]最大半连通子图 Tarjan+拓扑排序+dp
题目描述 一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:对于u,v∈V,满足u→v或v→u,即对于图中任意两点u,v,存在一条u到v的有向路径或者从v到u的有向路径. ...
- 【模拟7.16】通讯(tarjan缩点加拓扑排序)
这题确实水,纯板子,考试意外出错,只拿了暴力分QAQ tarjan缩点加上拓扑排序,注意这里求最短路径时不能用最小生成树 因为是单向边,不然就可能不是一个联通图了.... 1 #include< ...
- 【bzoj5017】[Snoi2017]炸弹 线段树优化建图+Tarjan+拓扑排序
题目描述 在一条直线上有 N 个炸弹,每个炸弹的坐标是 Xi,爆炸半径是 Ri,当一个炸弹爆炸时,如果另一个炸弹所在位置 Xj 满足: Xi−Ri≤Xj≤Xi+Ri,那么,该炸弹也会被引爆. 现在 ...
- 【Luogu P3387】缩点模板(强连通分量Tarjan&拓扑排序)
Luogu P3387 强连通分量的定义如下: 有向图强连通分量:在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶 ...
- 【BZOJ-1924】所驼门王的宝藏 Tarjan缩点(+拓扑排序) + 拓扑图DP
1924: [Sdoi2010]所驼门王的宝藏 Time Limit: 5 Sec Memory Limit: 128 MBSubmit: 787 Solved: 318[Submit][Stat ...
- bzoj1093[ZJOI2007]最大半连通子图(tarjan+拓扑排序+dp)
Description 一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:?u,v∈V,满足u→v或v→u,即对于图中任意两点u,v,存在一条u到v的有向路径或者从v到u ...
- 【tarjan 拓扑排序 dp】bzoj1093: [ZJOI2007]最大半连通子图
思维难度不大,关键考代码实现能力.一些细节还是很妙的. Description 一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:?u,v∈V,满足u→v或v→u,即对于 ...
- 洛谷P1073 Tarjan + 拓扑排序 // 构造分层图
https://www.luogu.org/problemnew/show/P1073 C国有 n n个大城市和 mm 条道路,每条道路连接这 nn个城市中的某两个城市.任意两个城市之间最多只有一条道 ...
- bzoj 1093 最大半连通子图 - Tarjan - 拓扑排序 - 动态规划
一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:?u,v∈V,满足u→v或v→u,即对于图中任意两点u,v,存在一条u到v的有向路径或者从v到u的有向路径.若G'=(V ...
随机推荐
- php改变时间的方法
1. strtotime date("Y-m-d",strtotime('+1day')) 2. mktime date("Y-m-d",mktime(0,0, ...
- TCP 协议详解
TCP 协议是 更靠近应用层,因此在应用程序中具有更强可操作性,一些重要 socket 选项都和 TCP 协议相关. TCP 头部信息:TCP 头部信息出现在每个 TCP 报文段中,用于指定通信的源端 ...
- TensorFlow——TensorBoard可视化
TensorFlow提供了一个可视化工具TensorBoard,它能够将训练过程中的各种绘制数据进行展示出来,包括标量,图片,音频,计算图,数据分布,直方图等,通过网页来观察模型的结构和训练过程中各个 ...
- lldb调试器知多少
lldb调试器简介 lldb 是一个有着 REPL 的特性和 C++ .Python 插件的开源调试器.lldb调试器的由来是伴随着Xcode的版本升级而来. Xcode4.3之前使用的默认调试器 ...
- Epplus Excel 导入 MSSQL 数据库
效果: 下载EXE 源码
- Nhibernate的Session和StatelessSession性能比较
Nhibernate的Session和StatelessSession性能比较 作者:Jesai 一个月入30K的大神有一天跟我说:我当年在你现在这个阶段,还在吊儿郎当呢!所以你努力吧! 有时候,一个 ...
- 9.Java三大版本以及JDK,JRE,JVM简单介绍
Write Once,Run Anywhere. JavaSE:标准版(桌面程序,控制台开发...),是Java的基础和核心. JavaME:嵌入式开发(手机,小家电...),现在基本不用已经过时. ...
- Knative 简介
原文地址:https://yq.aliyun.com/articles/658800
- Qt Installer Framework翻译(7-5)
操作 这些操作由组件和控制脚本准备,并由安装程序执行. 注意:操作是通过线程执行的. 在内部,每个操作都有一个DO步骤,包含有关安装程序的说明,以及一个UNDO步骤,包含有关卸载程序的说明. 操作总结 ...
- xpath写法大全(适用于selenium、robotframework)
1.//input[contains(@id, 'txttags')] 定位出来是个ID,但是ID后面的“102”是个随机数,所以用定位ID的方法就不行了,用firepath生成的xpath也会包括这 ...