2019/5/13 洛谷P4742 【tarjan缩点 + 拓扑dp】
题目链接:https://www.luogu.org/problemnew/show/P4742
题目大意:给一张有向图, 每个点都有点权,第一次经过该点时,该点的点权有贡献,求这张图上一条路径(终点随意)的贡献最大并且得到该路径上一个最大点权。
思路:
1.值得注意的是,这里并不是求最长路,也就是并不是求最多的点组成的路径,点可以少,但是必须点权和最大。
2.因为需要得到最大点权和以及最大点权,终点又不定,所以我们需要遍历图中每个点,来得到起点到该点的点权和以及路径上的最大点权。
3.先用tarjan进行缩点处理, 若不这样直接遍历原图非常耗时。缩点的‘点’中需要记录的信息是每个连通分量中点的权值和以及最大的点权。
4.还需要选择一个合理的遍历新图的方式, 那么选用 拓扑排序 ,(拓扑排序只有在设置汇点跳出的情况下得到的路径才不唯一,否则会根据点边关系遍历图中的每个点)
代码:
1 #include<stdio.h> 2 #include<string.h> 3 #include<stack> 4 #include<queue> 5 #include<algorithm> 6 #define mem(a, b) memset(a, b, sizeof(a)) 7 using namespace std; 8 const int inf = 0x3f3f3f3f; 9 const int maxn = 200010; 10 const int maxm = 500010; 11 typedef long long ll; 12 13 int n, m, in[maxn]; 14 int arr[maxn], max_node[maxn], sum_color[maxn]; 15 int cnt, new_cnt, head[maxn], new_head[maxn]; 16 int dfn[maxn], low[maxn], vis[maxn], belong[maxn], deep, color; 17 int dis[maxn], d_node[maxn];//dis代表从起点到该点的点权和,d_node代表起点到该点的点权最大值 18 stack<int>S; 19 queue<int>Q; 20 21 struct Edge 22 { 23 int to, next; 24 }edge[maxm], new_edge[maxm]; 25 26 void add(int a, int b) 27 { 28 edge[++ cnt].to = b; 29 edge[cnt].next = head[a]; 30 head[a] = cnt; 31 } 32 33 void new_add(int a, int b) 34 { 35 new_edge[++ new_cnt].to = b; 36 new_edge[new_cnt].next = new_head[a]; 37 new_head[a] = new_cnt; 38 } 39 40 void tarjan(int now) 41 { 42 dfn[now] = low[now] = ++ deep; 43 vis[now] = 1; 44 S.push(now); 45 for(int i = head[now]; i != -1; i = edge[i].next) 46 { 47 int to = edge[i].to; 48 if(!dfn[to]) 49 { 50 tarjan(to); 51 low[now] = min(low[now], low[to]); 52 } 53 else if(vis[to]) 54 low[now] = min(low[now], dfn[to]); 55 } 56 if(low[now] == dfn[now]) 57 { 58 color ++; 59 while(1) 60 { 61 int temp = S.top(); 62 S.pop(); 63 vis[temp] = 0; 64 belong[temp] = color; 65 max_node[color] = max(max_node[color], arr[temp]); //存每个强连通分量里亮度最大的点的值 66 sum_color[color] += arr[temp];//存每个强连通分量里的亮度和 67 if(temp == now) 68 break; 69 } 70 } 71 } 72 73 void topu_sort() 74 { 75 // mem(dis, -inf); 76 while(!Q.empty()) 77 Q.pop(); 78 for(int i = 1; i <= color; i ++)//topu_sort将入度为0的入队 ,并初始化d_node,每个分量的最大亮度 79 { 80 dis[i] = sum_color[i]; //初始化dis, d_node 81 d_node[i] = max_node[i]; 82 if(!in[i]) 83 Q.push(i); 84 } 85 while(!Q.empty()) 86 { 87 int temp = Q.front(); 88 Q.pop(); 89 for(int i = new_head[temp]; i != -1; i = new_edge[i].next) 90 { 91 int to = new_edge[i].to; 92 in[to] --; 93 if(!in[to]) 94 Q.push(to); 95 if(dis[to] <= dis[temp] + sum_color[to])//选择新入的点,那么d_node就要与新入的缩点比较了 96 { 97 dis[to] = dis[temp] + sum_color[to]; 98 d_node[to] = max(d_node[temp], max_node[to]); 99 } 100 /* 101 else if(dis[to] == dis[temp] + sum_color[to]) 102 d_node[to] = max(d_node[temp], max_node[to]); 103 */ 104 } 105 } 106 int sum = -1; 107 int maxx; 108 for(int i = 1; i <= color; i ++) 109 { 110 if(dis[i] > sum) 111 { 112 sum = dis[i]; 113 maxx = d_node[i]; 114 } 115 } 116 printf("%d %d\n", sum, maxx); 117 } 118 119 int main() 120 { 121 cnt = new_cnt = deep = color = 0; 122 mem(head, -1), mem(new_head, -1), mem(in, 0); 123 mem(vis, 0), mem(dfn, 0), mem(low, 0); 124 mem(max_node, -inf), mem(sum_color, 0); 125 scanf("%d%d", &n, &m); 126 for(int i = 1; i <= n; i ++) 127 scanf("%d", &arr[i]); //存每个点的光亮值 128 for(int i = 1; i <= m; i ++) 129 { 130 int a, b; 131 scanf("%d%d", &a, &b); 132 add(a, b); 133 } 134 for(int i = 1; i <= n; i ++) 135 if(!dfn[i]) 136 tarjan(i); 137 for(int i = 1; i <= n; i ++) 138 { 139 for(int j = head[i]; j != -1; j = edge[j].next) 140 { 141 int to = edge[j].to; 142 int x = belong[i], y = belong[to]; //将分量连边 缩点 注意是分量 belong 143 if(x != y) 144 { 145 new_add(x, y); 146 in[y] ++; 147 } 148 } 149 } 150 topu_sort(); 151 return 0; 152 }