[关闭]
@Gary-Ying 2018-08-01T22:57:11.000000Z 字数 2181 阅读 989

【Luogu3381】【模板】缩点

解题报告


题目描述

给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。

输入输出格式

输入格式:

第一行,n,m
第二行,n个整数,依次代表点权
第三至m+2行,每行两个整数u,v,表示u->v有一条有向边

输出格式:

共一行,最大的点权之和。

样例输入

  1. 2 2
  2. 1 1
  3. 1 2
  4. 2 1

样例输出

  1. 2

数据范围

,,

题解

算法

正如题目名称所说,这道题需要使用缩点的技巧,那么这是为什么呢?
我们可以把一个强连通分量近似看作一个环,由于这道题中边权只能取1次,每个点和每条边可以经过多次,所以一个强连通分量中的点一定可以全部取到。并且强连通分量中各点的出边可以看做是同一点的出边。于是我们可以高兴地说:

这道题中一个强连通分量相当于一个点,我们可以缩点了!

缩点以后图就变成了DAG(有向无环图),对于有向无环图,注定是要与拓扑排序为伴的。DP转移方程如下:(dp[v]表示走到v点的最大路径长度,a[v]表示v这个强连通分量中所有点的点权之和)

转移的条件是存在边(u,v)。用拓扑排序转移DP即可(避免后效性,保证处理v的转移时u的转移已经处理)。

易错点(其实就是我错的)

1、第37行:if (vis[v])low[u] = min(low[u], dfn[v]);
2、

  1. for (int i = 1; i <= n; ++i)
  2. if (!dfn[i])tarjan(i);

3、 第84行:addedge1(color[i],color[v]);

Code

  1. #include<iostream>
  2. #include<cstdio>
  3. using namespace std;
  4. const int maxn = 10007;
  5. const int maxm = 100007;
  6. int n, m;
  7. int c[maxn], a[maxn];
  8. int edgenum, head[maxn], Next[maxm], vet[maxm];
  9. int edgenum1, head1[maxn], Next1[maxm], vet1[maxm];
  10. int dfn[maxn], low[maxn], stamp;
  11. int stack[maxn], top, color[maxn], num;
  12. int init[maxn];
  13. bool vis[maxn];
  14. void addedge(int u, int v){
  15. ++edgenum;
  16. vet[edgenum] = v;
  17. Next[edgenum] = head[u];
  18. head[u] = edgenum;
  19. }
  20. void addedge1(int u, int v){
  21. ++edgenum1;
  22. vet1[edgenum1] = v;
  23. Next1[edgenum1] = head1[u];
  24. head1[u] = edgenum1;
  25. ++init[v];
  26. }
  27. void tarjan(int u){
  28. dfn[u] = low[u] = ++stamp;
  29. stack[++top] = u; vis[u] = true;
  30. for (int e = head[u]; e; e = Next[e]){
  31. int v = vet[e];
  32. if (!dfn[v]){
  33. tarjan(v);
  34. low[u] = min(low[u], low[v]);
  35. }else if (vis[v])low[u] = min(low[u], dfn[v]);
  36. }
  37. if (dfn[u] == low[u]){
  38. color[u] = ++num; vis[u] = false;
  39. while (stack[top] != u) vis[stack[top]] = false, color[stack[top--]] = num;
  40. --top;
  41. }
  42. }
  43. void tp(){
  44. int head = 0, tail = -1, que[maxn], dp[maxn];
  45. for (int i = 1; i <= num; ++i) {
  46. if (init[i] == 0){
  47. que[++tail] = i;
  48. }
  49. dp[i] = a[i];
  50. }
  51. while (head <= tail){
  52. int u = que[head]; ++head;
  53. for (int e = head1[u]; e; e = Next1[e]){
  54. int v = vet1[e];
  55. --init[v];
  56. if (init[v] == 0) que[++tail] = v;
  57. dp[v] = max(dp[v], dp[u] + a[v]);
  58. }
  59. }
  60. int ans = 0;
  61. for (int i = 1; i <= num; ++i)
  62. if (dp[i] > ans) ans = dp[i];
  63. printf("%d\n", ans);
  64. }
  65. int main(){
  66. scanf("%d%d", &n, &m);
  67. for (int i = 1; i <= n; ++i)
  68. scanf("%d", &c[i]);
  69. for (int i = 1; i <= m; ++i){
  70. int u, v;
  71. scanf("%d%d", &u, &v);
  72. addedge(u,v);
  73. }
  74. for (int i = 1; i <= n; ++i)
  75. if (!dfn[i])tarjan(i);
  76. for (int i = 1; i <= n; ++i){
  77. for (int e = head[i]; e; e = Next[e]){
  78. int v = vet[e];
  79. if (color[i]!=color[v]){
  80. addedge1(color[i],color[v]);
  81. }
  82. }
  83. a[color[i]] += c[i];
  84. }
  85. tp();
  86. return 0;
  87. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注