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 ...
随机推荐
- js字符数组转化为数字数组
先谈一谈需求,我有一个字符串数组,数组内容是字符类型的数字,需要把字符类型的数字转换为数字类型,找了找资料,发现牛人真是多,把前辈的经验总结一下. 例子 ['1','2','3'] => [1, ...
- 速石科技携HPC混合云平台亮相AWS技术峰会2019上海站
2019年6月20日,全球云技术盛会——AWS技术峰会2019(上海站)在上海世博中心举行.作为AWS的技术合作伙伴,速石科技携旗下基于混合云的一站式高性能计算(HPC)平台首次公开亮相. 速石科技向 ...
- Django设置 DEBUG=False后静态文件无法加载解决
前段时间调试一直是在Debug=True先运行的,没有什么问题.今天关闭了Debug后,出现了一个问题.就是静态文件找不到了,「img.css.js」都提示404,无法准确的访问 static 静态文 ...
- java项目Jenkins部署
假设背景: Nginx跳板机服务器:192.168.10.1 Tomcat应用服务器:192.168.10.3 端口:10083 应用名称:appXXX 1.配置跳板机的转发路径 如:192.168. ...
- 用HttpURLConnection来完成HTTP发送报文接收报文!
public String sendMsg(String url, byte[] PostData) { String content = null; URL urls = null; try { u ...
- 【WPF学习】第十三章 理解路由事件
每个.NET开发人员都熟悉“事件”的思想——当有意义的事情发生时,由对象(如WPF元素)发送的用于通知代码的消息.WPF通过事件路由(event routing)的概念增强了.NET事件模型.事件路由 ...
- Markdown 标记 粘贴到 小书 匠 才知道 哦
# 一级标题## 这是二级标题### 三级标题##### 五级 高阶== 低阶-- [TOC] > 这是一级引用>>这是二级引用>>> 这是三级引用 ```java ...
- Mixing .NET
- 源码的说明 ASP.NET MVC 5框架揭秘.zip
第1章 S101 MVP(SC)模式中Presenter与View之间的交互 S102 迷你版的ASP.NET MVC框架 第2章 S201 通过路由实现请求地址与.aspx页面的映射 S202 基本 ...
- UML--->用例图梳理
用例图梳理 概述 用例图主要用来描述"用户.需求.系统功能单元"之间的关系.它展示了一个外部用户能够观察到的系统功能模型图. 用例图的主要目的是帮助开发团队以一种可视化的方式理解系 ...