洛谷 P3627 [APIO2009]抢掠计划 Tarjan缩点+Spfa求最长路
题目地址:https://www.luogu.com.cn/problem/P3627
第一次寒假训练的结测题,思路本身不难,但对于我这个码力蒟蒻来说实现难度不小…考试时肛了将近两个半小时才刚肛出来。我也是吐了
题面
Siruseri 城中的道路都是单向的。不同的道路由路口连接。按照法律的规定, 在每个路口都设立了一个 Siruseri 银行的 ATM 取款机。令人奇怪的是,Siruseri 的酒吧也都设在路口,虽然并不是每个路口都设有酒吧。
Banditji 计划实施 Siruseri 有史以来最惊天动地的 ATM 抢劫。他将从市中心 出发,沿着单向道路行驶,抢劫所有他途径的 ATM 机,最终他将在一个酒吧庆 祝他的胜利。
使用高超的黑客技术,他获知了每个 ATM 机中可以掠取的现金数额。他希 望你帮助他计算从市中心出发最后到达某个酒吧时最多能抢劫的现金总数。他可 以经过同一路口或道路任意多次。但只要他抢劫过某个 ATM 机后,该 ATM 机 里面就不会再有钱了。 例如,假设该城中有 6 个路口,道路的连接情况如下图所示:
市中心在路口 1,由一个入口符号→来标识,那些有酒吧的路口用双圈来表
示。每个 ATM 机中可取的钱数标在了路口的上方。在这个例子中,Banditji 能抢 劫的现金总数为 47,实施的抢劫路线是:1-2-4-1-2-3-5。
输入格式
第一行包含两个整数 N、M。N 表示路口的个数,M 表示道路条数。接下来 M 行,每行两个整数,这两个整数都在 1 到 N 之间,第 i+1 行的两个整数表示第 i 条道路的起点和终点的路口编号。接下来 N 行,每行一个整数,按顺序表示每 个路口处的 ATM 机中的钱数。接下来一行包含两个整数 S、P,S 表示市中心的 编号,也就是出发的路口。P 表示酒吧数目。接下来的一行中有 P 个整数,表示 P 个有酒吧的路口的编号。
样例输入:
6 7
1 2
2 3
3 5
2 4
4 1
2 6
6 5
10
12
8
16
1
5
1 4
4 3 5 6
输出格式
输出一个整数,表示 Banditji 从市中心开始到某个酒吧结束所能抢劫的最多 的现金总数。
样例输出:47
思路:
由题给的图可以看出来,图中存在环,而每个边和点都可以重复走,那么我们可以从从环的任意一点起,无限制地到达环上的任意一点,所以我们可以把整个环看成一个点,环点的权是环上点权的和,这很容易联想到Tarjan缩点把原图化成DAG图;
然后题目让求最大的总权值,最常规的做法就是用Spfa跑最长路;
(有位很厉害的学长改装了Dijskra…,并坚信它能跑DAG的最长路,我们戏称为堆劣化Spfa…遗憾的是我并没有学会…)
这样,我们确定了大体的思路:Tarjan缩点+Spfa求最长路
代码的实现(重头戏来了)
emm…看看这堆数组就能明白这题有点恶心
所以我觉得分步来讲比较好
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=5*1e5+10;
int n,m,dis[maxn],money[maxn];
bool inq[maxn];
int from[maxn],to[maxn];
int head[maxn],dfn[maxn],low[maxn],dfs_clock=0;
int sta[maxn],scc_cnt=0,siz[maxn],top=0,belong[maxn],len=0,cnt[maxn];
- 前置准备
前向星存图,基本操作:
struct Edge{
int from,to,next,dis;
}edge[maxn*2];
void Add(int a,int b,int c){
edge[++len].to=b;
edge[len].from=a;
edge[len].next=head[a];
edge[len].dis=c;
head[a]=len;
}
- Tarjan函数 这里我用的是数组模拟栈,即sta数组,top为栈顶; 其他的和普通的Tarjan模板没甚不一样
void Tarjan(int u){
dfn[u]=low[u]=++dfs_clock;
sta[++top]=u;//++top不要写成top++
for(int i=head[u];i;i=edge[i].next){
int v=edge[i].to;
if(!dfn[v]){
Tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(!belong[v]) low[u]=min(low[u],dfn[v]);
}//这里省去了一个bool型vis数组,因为v如果入了栈,belong[v]的值
//会被修改,不再是0,这就跟vis的效果一样
if(dfn[u]==low[u]){
int f=++scc_cnt;//scc_cnt记录强连通分量的个数
while(true){
int x=sta[top--];
belong[x]=f;
siz[f]+=money[x];//siz数组记录每个环的总权值
if(x==u) break;
}
}
}
- 主函数部分
Spfa函数涉及到一些小细节,所以先上主函数再说Spfa
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
//这里最好是开两个数组把边记下来,一会建图会用到
scanf("%d%d",&from[i],&to[i]);
//懒人为了少写一个Add函数,直接用0当权值
Add(from[i],to[i],0);
}
for(int i=1;i<=n;i++) scanf("%d",&money[i]);
for(int i=1;i<=n;i++)//缩点,常规操作
if(!dfn[i]) Tarjan(i);
New();//建图函数,下步就说
int U,P;scanf("%d%d",&U,&P);
Spfa(U);//下下步说
int ans=0;
for(int i=1;i<=P;i++){
//遍历各个酒馆,找最远的那个就行
int x;scanf("%d",&x);
ans=max(ans,dis[belong[x]]);
}
printf("%d\n",ans);
return 0;
}
- New函数
这个函数是建一个缩点后的新图让Spfa跑
void New(){
//为节省空间直接清空原图就好,不用再新开
memset(edge,0,sizeof(edge));
memset(head,0,sizeof(head));
len=0;
for(int i=1;i<=m;i++){
//同环内的点无需存
if(belong[from[i]]!=belong[to[i]]){
//这里我直接存了环的原权,如果愿意也可以存负权跑最短路
Add(belong[from[i]],belong[to[i]],siz[belong[to[i]]]);
}
}
}
可能有人注意到我这里在存边权时只存了belong[to[i]]的权,也就是边指向的那个点所在的环的权,那from所在的环的权又该怎办呢
那么,这就是一会Spfa需要注意的细节了(其实就一行…)
Spfa函数(重重头戏来了)
大部分和Spfa的板子没区别,要点写在了注释里
queue<int> q;
void Spfa(int u){
for(int i=1;i<=scc_cnt;i++){
dis[i]=0;
inq[i]=false;
cnt[i]=0;
}
//这里就是全题最精髓也是最不容易注意的地方...
//首先,我们并不确定起点u的位置,它可能是一个独立的点,也可能
//属于一个环,所以,我们要把u直接当成环处理
int U=belong[u];
//再者,因为我们的边权只存了“去环”的权,那么原环的权就丢失了
//所以这里dis的起点不再是0,而是起始点的环权,避免了权丢失
dis[U]=siz[U];
cnt[U]=1;
inq[U]=1;
q.push(U);
while(!q.empty()){
int uu=q.front();q.pop();
inq[uu]=false;
for(int i=head[uu];i;i=edge[i].next){
int v=edge[i].to;
//求最长路,所以是<,最短路则是>
if(dis[v]<dis[uu]+edge[i].dis){
dis[v]=dis[uu]+edge[i].dis;
if(!inq[v]){
inq[v]=true;
q.push(v);
}
}
}
}
}//每次看到spfa后面这一长串括号就觉得心慌
这样,代码的各部分就完成了。纵观整道题,我们发现思路并不复杂,代码都是由基础的模板拼上的,我们所要重点考虑的是两者结合后会相互对对方产生怎样的影响,这里主要就是Tarjan缩点后对边权的影响
(考试的时候我为了找这个错找了两个小时…完蛋玩意儿)
最后
放上全部代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=5*1e5+50;
int n,m,dis[maxn],money[maxn];
bool inq[maxn];
int from[maxn],to[maxn];
int head[maxn],dfn[maxn],low[maxn],dfs_clock=0;
int sta[maxn],scc_cnt=0,siz[maxn],top=0,belong[maxn],len=0,cnt[maxn];
struct Edge{
int from,to,next,dis;
}edge[maxn*2];
void Add(int a,int b,int c){
edge[++len].to=b;
edge[len].from=a;
edge[len].next=head[a];
edge[len].dis=c;
head[a]=len;
}
void Tarjan(int u){
dfn[u]=low[u]=++dfs_clock;
sta[++top]=u;
for(int i=head[u];i;i=edge[i].next){
int v=edge[i].to;
if(!dfn[v]){
Tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(!belong[v]) low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
int f=++scc_cnt;
while(true){
int x=sta[top--];
belong[x]=f;
siz[f]+=money[x];
if(x==u) break;
}
}
}
queue<int> q;
void Spfa(int u){
for(int i=1;i<=scc_cnt;i++){
dis[i]=0;
inq[i]=false;
cnt[i]=0;
}
int U=belong[u];
cnt[U]=1;dis[U]=siz[U];inq[U]=1;
q.push(U);
while(!q.empty()){
int uu=q.front();q.pop();
inq[uu]=false;
for(int i=head[uu];i;i=edge[i].next){
int v=edge[i].to;
if(dis[v]<dis[uu]+edge[i].dis){
dis[v]=dis[uu]+edge[i].dis;
if(!inq[v]){
inq[v]=true;
q.push(v);
}
}
}
}
}
void New(){
memset(edge,0,sizeof(edge));
memset(head,0,sizeof(head));
len=0;
for(int i=1;i<=m;i++){
if(belong[from[i]]!=belong[to[i]]){
Add(belong[from[i]],belong[to[i]],siz[belong[to[i]]]);
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&from[i],&to[i]);
Add(from[i],to[i],0);
}
for(int i=1;i<=n;i++) scanf("%d",&money[i]);
for(int i=1;i<=n;i++)
if(!dfn[i]) Tarjan(i);
New();
int U,P;scanf("%d%d",&U,&P);
Spfa(U);
int ans=0;
for(int i=1;i<=P;i++){
int x;scanf("%d",&x);
ans=max(ans,dis[belong[x]]);
}
printf("%d\n",ans);
return 0;
}
小小萌新第一次写博客,感觉还是比较认真的
如果有大佬看见什么不对的地方还请在法律允许的范围内嘲笑我orz
溜了溜了
洛谷 P3627 [APIO2009]抢掠计划 Tarjan缩点+Spfa求最长路的更多相关文章
- 【题解】洛谷P3627 [APIO2009]抢掠计划(缩点+SPFA)
洛谷P3627:https://www.luogu.org/problemnew/show/P3627 思路 由于有强连通分量 所以我们可以想到先把整个图缩点 缩点完之后再建一次图 把点权改为边权 并 ...
- 洛谷 P3627 [APIO2009]抢掠计划 题解
Analysis 建图+强连通分量+SPFA求最长路 但要保证最后到达的点中包含酒馆 虽然思路并不难想,但要求的代码能力很高. #include<iostream> #include< ...
- [洛谷P3627][APIO2009]抢掠计划
题目大意:给你一张$n(n\leqslant5\times10^5)$个点$m(m\leqslant5\times10^5)$条边的有向图,有点权,给你起点和一些可能的终点.问从起点开始,到任意一个终 ...
- 洛谷 P3627 [APIO2009]抢掠计划
这题一看就是缩点,但是缩完点怎么办呢?首先我们把所有的包含酒吧的缩点找出来,打上标记,然后建立一张新图, 每个缩点上的点权就是他所包含的所有点的点权和.但是建图的时候要注意,每一对缩点之间可能有多条边 ...
- [luogu3627 APIO2009] 抢掠计划 (tarjan缩点+spfa最长路)
传送门 Description Input 第一行包含两个整数 N.M.N 表示路口的个数,M 表示道路条数.接下来 M 行,每行两个整数,这两个整数都在 1 到 N 之间,第 i+1 行的两个整数表 ...
- [APIO2009]抢掠计划 tarjan缩点+spfa BZOJ1179
题目描述 Siruseri 城中的道路都是单向的.不同的道路由路口连接.按照法律的规定, 在每个路口都设立了一个 Siruseri 银行的 ATM 取款机.令人奇怪的是,Siruseri 的酒吧也都设 ...
- 洛谷 P3627 【抢掠计划】
题库:洛谷 题号:3627 题目:抢掠计划 link:https://www.luogu.org/problem/P3627 思路 : 这道题是一道Tarjan + 最长路的题.首先,我们用Tarja ...
- BZOJ1179或洛谷3672 [APIO2009]抢掠计划
BZOJ原题链接 洛谷原题链接 在一个强连通分量里的\(ATM\)机显然都可被抢,所以先用\(tarjan\)找强连通分量并缩点,在缩点的后的\(DAG\)上跑最长路,然后扫一遍酒吧记录答案即可. # ...
- 洛谷3627 [APIO2009]抢掠计划
题目描述 输入格式: 第一行包含两个整数 N.M.N 表示路口的个数,M 表示道路条数.接下来 M 行,每行两个整数,这两个整数都在 1 到 N 之间,第 i+1 行的两个整数表示第 i 条道路的起点 ...
随机推荐
- .NET Core技术研究系列-索引篇
随着.NET Core相关技术研究的深入,现在将这一系列的文章,整理到一个索引页中,方便大家翻阅查找,同时,后续也会不断补充进来. .NET Core技术研究-WebApi迁移ASP.NET Core ...
- Kubernetes日志的6个最佳实践
本文转自Rancher Labs Kubernetes可以帮助管理部署在Pod中的上百个容器的生命周期.它是高度分布式的并且各个部分是动态的.一个已经实现的Kubernetes环境通常涉及带有集群和节 ...
- Python 抓取网页tag操作
1. 获取操作tag 获取操作tag的接种方式: soup.find_all(name=None, attrs={}, recursive=True, text=None, limit=None, * ...
- 机器学习——手把手教你用Python实现回归树模型
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天这篇是机器学习专题的第24篇文章,我们来聊聊回归树模型. 所谓的回归树模型其实就是用树形模型来解决回归问题,树模型当中最经典的自然还是决 ...
- [AHOI2017/HNOI2017]单旋
题目 点这里看题目. 分析 最妙的地方在于,这道题其实是用一种数据结构模拟另一种数据结构! 我们需要维护深度和树的结构,以下对于每个操作进行分别讨论. 插入一个新节点 可以发现,这个新 ...
- conda 管理包
查看当前环境已经安装的包 conda list 查看指定环境中的安装包 conda list -n python27 在Anaconda 库中搜索包 conda search numpy #模糊搜索 ...
- PytorchMNIST(使用Pytorch进行MNIST字符集识别任务)
都说MNIST相当于机器学习界的Hello World.最近加入实验室,导师给我们安排了一个任务,但是我才刚刚入门呐!!没办法,只能从最基本的学起. Pytorch是一套开源的深度学习张量库.或者我倾 ...
- Android学习笔记上下文菜单
布局文件main_activity.xml <?xml version="1.0" encoding="utf-8"?> <RelativeL ...
- C#数据结构与算法系列(八):栈(Stack)
1.介绍 栈是一个先入后出(FILO-First In Last Out)的有序列表 栈是限制线性表中元素的插入和删除只能在线性表的同一端进行的特殊线性表.允许插入和删除的一端,为变化的一端,称为栈顶 ...
- zip矩阵转至
list01=[1,2,3,4] list02=["a","b","c","d"] for itme in zip(li ...