手动博客搬家: 本文发表于20170716 10:58:18, 原地址https://blog.csdn.net/suncongbo/article/details/81061601

https://www.lydsy.com/JudgeOnline/problem.php?id=1179

题意:给定一张有向图,点有点权。给定起点(仅一个)\(s\)与终点集合(多个)\(T\),问开始于起点、结束于终点集合中的一个点的路径中点权和的最大值。

题解:这里,第一步当然是tarjan缩点。

由于是开始于特定点,dp有些麻烦,建议采用spfa. 但是DP也能做,而且bfs和dfs都能做。只不过,有一些需要注意的地方,很容易写错。

bfs状态转移方程很简单:\(dp[i]\)表示从s出发到i这个点路径点权和的最大值,\(dp[i]=max_{j\in ind[i]}{dp[j]+a[i]}\), a[i]为点权。对于终点集合,我们可以建一个超级终点,每一个终点向超级终点连边,当然也可以朴素地枚举每个终点。但是,有一个细节问题坑了我很久:如果是这样写的:

  1. Tarjan原图中的每一个点;
  2. 建新图;
  3. bfs(); (队列中初始只有一个元素s)
  4. ans = dp[超级终点];

则必然会WA. 后来我又对着标程查了好久,问了几位大佬,最后实在没办法对拍了一下,发现生成了这样一组数据成功卡掉自己,经简化如下——

  1. 3 2
  2. 1 2
  3. 3 2
  4. 5
  5. 1
  6. 3
  7. 1 1
  8. 2
  9. Read 0,Expected 6.

正确的写法应该是:

  1. Tarjan(s); //从s能到达的点
  2. 建新图;
  3. bfs(); (队列中初始只有一个元素s)
  4. ans = dp[超级终点];

只有第一行tarjan不一样。为什么呢?

原因是:对于一个点i, 刚才我们讨论的数食物链、字母最多出现次数、最长路径,都是要求\(i\)的所有入边的起点都被更新后才可转移\(i\)(将其加入队列),bfs的时候从所有入度为0的点开始。但是,现在情况变了,如果我们只从s开始bfs,有一些s走不到的点将永远不会被更新,这样这个点所连向的边也不会被更新,就相当于这个点作废了。但是实际上这个点并未作废。如图所示,如果1号点为起点,2号点为终点,那3号点显然不会被访问到,但是由于2号点有1->2, 3->2两条入边,因此3号点不更新,2号点永远也不会被更新,答案一直是0.但是实际上,2还应该被1更新,因为起点1一定可以通过1->2进入2号点。

实际上,我们确保这种入边的起点先统计的顺序,原因是防止一个点有许多条转移的途径,但是只枚举了其中的一部分,最优解藏在另一部分中的这种情况。但是在这里,所谓“另一部分”是s点永远不可能达到的,因此答案为0,不可能成为最优解,反而还会拖累2号点的更新。



AC代码:

  1. #include<cstdio>
  2. #include<algorithm>
  3. using namespace std;
  4. const int N = 500000;
  5. struct Edge
  6. {
  7. int v,nxt; bool us;
  8. } e[N+2],e0[N+2];
  9. int fe[N+2],fe0[N+2];
  10. int a[N+2],a0[N+2];
  11. bool f[N+2];
  12. int dfn[N+2],low[N+2];
  13. int sta[N+2];
  14. bool ins[N+2];
  15. int clr[N+2];
  16. int dp[N+2];
  17. int que[N+2];
  18. int ind[N+2];
  19. bool f0[N+2];
  20. int n,m,m0,s,p,tp,cnt,num;
  21. void addedge(int u,int v)
  22. {
  23. m++; e[m].v = v;
  24. e[m].nxt = fe[u]; fe[u] = m;
  25. }
  26. void addedge0(int u,int v)
  27. {
  28. m0++; e0[m0].v = v; ind[v]++;
  29. e0[m0].nxt = fe0[u]; fe0[u] = m0;
  30. }
  31. void Tarjan(int u)
  32. {
  33. cnt++; dfn[u] = low[u] = cnt; ins[u] = true;
  34. tp++; sta[tp] = u;
  35. for(int i=fe[u]; i; i=e[i].nxt)
  36. {
  37. if(dfn[e[i].v]==0) {Tarjan(e[i].v); low[u] = min(low[u],low[e[i].v]);}
  38. else if(ins[e[i].v]==true) {low[u] = min(low[u],dfn[e[i].v]);}
  39. }
  40. if(low[u]==dfn[u])
  41. {
  42. num++; clr[u] = num; a0[num] = a[u]; f0[num] = f[u];
  43. while(sta[tp]!=u)
  44. {
  45. ins[sta[tp]] = false;
  46. clr[sta[tp]] = num;
  47. a0[num] += a[sta[tp]];
  48. f0[num] = f[sta[tp]]||f0[num];
  49. tp--;
  50. }
  51. ins[u] = false; tp--;
  52. }
  53. }
  54. void bfs()
  55. {
  56. int head = 1,tail = 1; que[tail] = clr[s]; dp[clr[s]] = a0[clr[s]];
  57. while(head<=tail)
  58. {
  59. int cur = que[head]; head++;
  60. for(int i=fe0[cur]; i; i=e0[i].nxt)
  61. {
  62. if(e0[i].us==false)
  63. {
  64. ind[e0[i].v]--; e0[i].us = true; dp[e0[i].v] = max(dp[e0[i].v],dp[cur]+a0[e0[i].v]);
  65. if(ind[e0[i].v]==0)
  66. {
  67. tail++; que[tail] = e0[i].v;
  68. }
  69. }
  70. }
  71. }
  72. }
  73. int main()
  74. {
  75. int mm; m = 0; scanf("%d%d",&n,&mm);
  76. for(int i=1; i<=mm; i++)
  77. {
  78. int x,y; scanf("%d%d",&x,&y); addedge(x,y);
  79. }
  80. for(int i=1; i<=n; i++) scanf("%d",&a[i]);
  81. scanf("%d%d",&s,&p);
  82. for(int i=1; i<=p; i++) {int x; scanf("%d",&x); f[x] = true;}
  83. cnt = 0; Tarjan(s);
  84. //for(int i=1; i<=n; i++) if(!dfn[i]) Tarjan(i);
  85. num++;
  86. for(int i=1; i<=n; i++)
  87. {
  88. for(int j=fe[i]; j; j=e[j].nxt)
  89. {
  90. if(clr[i]!=0 && clr[e[j].v]!=0 && clr[i]!=clr[e[j].v])
  91. {
  92. addedge0(clr[i],clr[e[j].v]);
  93. }
  94. }
  95. }
  96. for(int i=1; i<=num; i++) {if(f0[i]==true) addedge0(i,num);}
  97. bfs();
  98. printf("%d\n",dp[num]);
  99. return 0;
  100. }

dfs当然也能做,此处不再赘述。

BZOJ 1179 抢掠计划atm (缩点+有向无环图DP)的更多相关文章

  1. bzoj 1179 [APIO 2009]Atm(APIO水题) - Tarjan - spfa

    Input 第一行包含两个整数N.M.N表示路口的个数,M表示道路条数.接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道路的起点和终点的路口编号.接下来N行,每行一 ...

  2. [APIO2009]抢掠计划 tarjan缩点+spfa BZOJ1179

    题目描述 Siruseri 城中的道路都是单向的.不同的道路由路口连接.按照法律的规定, 在每个路口都设立了一个 Siruseri 银行的 ATM 取款机.令人奇怪的是,Siruseri 的酒吧也都设 ...

  3. 洛谷 P3627 [APIO2009]抢掠计划 Tarjan缩点+Spfa求最长路

    题目地址:https://www.luogu.com.cn/problem/P3627 第一次寒假训练的结测题,思路本身不难,但对于我这个码力蒟蒻来说实现难度不小-考试时肛了将近两个半小时才刚肛出来. ...

  4. [luogu3627 APIO2009] 抢掠计划 (tarjan缩点+spfa最长路)

    传送门 Description Input 第一行包含两个整数 N.M.N 表示路口的个数,M 表示道路条数.接下来 M 行,每行两个整数,这两个整数都在 1 到 N 之间,第 i+1 行的两个整数表 ...

  5. 【题解】洛谷P3627 [APIO2009]抢掠计划(缩点+SPFA)

    洛谷P3627:https://www.luogu.org/problemnew/show/P3627 思路 由于有强连通分量 所以我们可以想到先把整个图缩点 缩点完之后再建一次图 把点权改为边权 并 ...

  6. 【Luogu】P3627抢掠计划(缩点最短路)

    题目链接在此 有环当然一定尽量走环,这是搞缩点的人都知道的常识. 建了新图之后搞点权SPFA跑最长路.枚举每个酒吧选择最大值. 发现我的博客写的越来越水了 #include<cstdio> ...

  7. 【BZOJ 1179】[Apio2009]Atm

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] tarjan强连通缩点一下. 然后把缩点之后,每个点的钱的数累加起来. 然后从S出发 开始一边做bfs一遍做dp. 最后输出有酒吧的 ...

  8. 【模板整合计划】图论—有向无环图 (DAG) 与树

    [模板整合计划]图论-有向无环图 (DAG) 与树 一:[拓扑排序] 最大食物链计数 \(\text{[P4017]}\) #include<cstring> #include<cs ...

  9. BZOJ 1179 (Tarjan缩点+DP)

    题面 传送门 分析 由于一个点可以经过多次,显然每个环都会被走一遍. 考虑缩点,将每个强连通分量缩成一个点,点权为联通分量上的所有点之和 缩点后的图是一个有向无环图(DAG) 可拓扑排序,按照拓扑序进 ...

随机推荐

  1. Python 网络爬虫与信息获取(二)—— 页面内容提取

    1. 获取超链接 python获取指定网页上所有超链接的方法 links = re.findall(b'"((http|ftp)s?://.*?)"', html) links = ...

  2. codeforces 931E Logical Expression dp

    time limit per test 3 seconds memory limit per test 256 megabytes input standard input output standa ...

  3. Spark入门之DataFrame/DataSet

    目录 Part I. Gentle Overview of Big Data and Spark Overview 1.基本架构 2.基本概念 3.例子(可跳过) Spark工具箱 1.Dataset ...

  4. Django day08 多表操作 (三) 基于对象的跨表查询 基于双下划线的多表查询

    一: 基于对象的跨表查询 1. 一对一 正向: 反向: 2. 一对多 正向: 反向: 3.多对多 正向: 反向: 4.*****基于对象的多表查询 二: 基于双下划线的多表查询 1. 连表查询 一对一 ...

  5. Aspnet_Session

    cmd: aspnet_regsql.exe -ssadd -sstype c -d ZZCasSession -S 192.168.0.3 -U sa -P szhweb2010 <!--会话 ...

  6. ASP.NET的Eval方法和Bind方法的区别

    Eval是只读的方法(单向数据在邦定),所邦定的内容为不会提交回服务器. 比如图书的ISBN,并不想让用户做任何修改,可以使用<%# Eval('ISBN').TOString().Trim() ...

  7. HTML+CSS(11)

    n  CSS背景属性 Background-color:背景色. Background-image:背景图片地址.如:background-image:url(images/bg.gif;) Back ...

  8. MongoDB安装使用教程

    参考菜鸟教程:http://www.runoob.com/mongodb/mongodb-tutorial.html

  9. 从XMLHttpRequest中获取请求的URL

    在编写Ajax通用错误处理程序时,经常需要记录发生错误的XMLHttpRequest的请求URL.但查询文档,并未找到从XMLHttpRequest中获取请求URL的方法. 在javascript - ...

  10. 学习廖雪峰的Python教程之数据类型

    数据类型 计算机顾名思义就是可以做数学计算的机器,因此,计算机程序理所当然地可以处理各种数值.但是,计算机能处理的远不止数值,还可以处理文本.图形.音频.视频.网页等各种各样的数据,不同的数据,需要定 ...