博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
洛谷 P3627 [APIO2009](抢掠计划 缩点+spfa)
阅读量:4328 次
发布时间:2019-06-06

本文共 3783 字,大约阅读时间需要 12 分钟。

题目描述

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 个有酒吧的路口的编号。

 

输出格式:

 

输出一个整数,表示 Banditji 从市中心开始到某个酒吧结束所能抢劫的最多 的现金总数。

 

输入输出样例

输入样例#1:
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
输出样例#1:
47

说明

50%的输入保证 N, M<=3000。所有的输入保证 N, M<=500000。每个 ATM 机中可取的钱数为一个非负整数且不超过 4000。

输入数据保证你可以从市中心 沿着 Siruseri 的单向的道路到达其中的至少一个酒吧。

 

这道题略加分析,便可知是强连通+最长路径。

强连通可以用tarjan,最长路可以用拓扑或spfa.

两个裸的算法。

注意:在强连通中,我们可以直接使用点权进行缩点

但在最长路径中,我们就得将点权等效替换成边权进行计算;

 

 

1 #include
2 using namespace std; 3 #define man 500010 4 /*tarjan*/ 5 int dfn[man],low[man],sta[man],bel[man],msum[man]; 6 int dep=0,top=0,cnt=0; 7 bool vis[man]; 8 /*edge*/ 9 int head[man<<2],num=0,dis[man]; 10 struct edge 11 { 12 int next,to,dis; 13 }e[man<<2]; 14 inline void add(int from,int to,int dis) 15 { 16 e[++num].next=head[from]; 17 e[num].to=to; 18 e[num].dis=dis; 19 head[from]=num; 20 } 21 inline void clear() 22 { 23 memset(e,0,sizeof(e)); 24 memset(head,0,sizeof(head)); 25 memset(vis,0,sizeof(vis)); 26 num=0; 27 } 28 /*common*/ 29 int n,m,cost[man],id[man],st,p,x[man],y[man]; 30 inline void tarjan(int s) 31 { 32 dfn[s]=low[s]=++dep; 33 vis[s]=1;sta[++top]=s; 34 for(int i=head[s];i;i=e[i].next) 35 { int to=e[i].to; 36 if(!dfn[to]) 37 { tarjan(to); 38 low[s]=min(low[s],low[to]); 39 } 40 else if(vis[to]) 41 low[s]=min(low[s],dfn[to]); 42 } 43 if(low[s]==dfn[s])//需熟练掌握这一段的写法 44 { int j;++cnt; 45 do 46 { j=sta[top--]; 47 bel[j]=cnt;//缩点 48 msum[cnt]+=cost[j];//计算联通块的权值 49 vis[j]=0; 50 }while(j!=s); 51 } 52 } 53 54 inline void spfa(int s) 55 { queue
q; 56 for(int i=1;i<=cnt;i++) dis[i]=12457812; 57 dis[s]=0;vis[s]=1;q.push(s); 58 do 59 { int u=q.front();q.pop();vis[u]=0; 60 for(int i=head[u];i;i=e[i].next) 61 { int to=e[i].to; 62 if(dis[to]>dis[u]+e[i].dis) 63 { dis[to]=dis[u]+e[i].dis; 64 if(!vis[to]) 65 { vis[to]=1; 66 q.push(to); 67 } 68 } 69 } 70 }while(q.size()); 71 } 72 73 int main() 74 { 75 ios::sync_with_stdio(false); 76 cin>>n>>m; 77 for(int i=1;i<=m;i++) 78 { 79 cin>>x[i]>>y[i]; 80 add(x[i],y[i],1); 81 } 82 for(int i=1;i<=n;i++) 83 cin>>cost[i]; 84 cin>>st>>p; 85 for(int i=1;i<=p;i++) 86 cin>>id[i]; 87 for(int i=1;i<=n;i++) 88 if(!dfn[i]) 89 tarjan(i); 90 clear(); 91 for(int i=1;i<=m;i++)//通过将边权取反求出最长路 92 { 93 if(bel[x[i]]!=bel[y[i]]) 94 { 95 add(bel[x[i]],bel[y[i]],-msum[bel[y[i]]]); 96 } 97 } 98 spfa(bel[st]); 99 int ans=-1000000;100 for(int i=1;i<=p;i++)101 { 102 ans=max(ans,-dis[bel[id[i]]]);}103 cout<
<

 

发现自己的强连通分量写的不熟练,需大量练习。

转载于:https://www.cnblogs.com/Slager-Z/p/7674860.html

你可能感兴趣的文章
20145303刘俊谦 Exp7 网络欺诈技术防范
查看>>
原生和jQuery的ajax用法
查看>>
iOS开发播放文本
查看>>
20145202马超《java》实验5
查看>>
JQuery 事件
查看>>
main(argc,argv[])
查看>>
在线教育工具—白板系统的迭代1——bug监控排查
查看>>
121. Best Time to Buy and Sell Stock
查看>>
hdu 1005 根据递推公式构造矩阵 ( 矩阵快速幂)
查看>>
安装php扩展
查看>>
百度移动搜索主要有如下几类结果构成
查看>>
Python爬虫面试题170道:2019版【1】
查看>>
JavaBean规范
查看>>
第四阶段 15_Linux tomcat安装与配置
查看>>
NAS 创建大文件
查看>>
学习笔记-模块之xml文件处理
查看>>
接口测试用例
查看>>
面试:用 Java 实现一个 Singleton 模式
查看>>
Sybase IQ导出文件的几种方式
查看>>
案例:手动输入一个字符串,打散放进一个列表,小写字母反序 大写字母保持不变...
查看>>