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 }
View Code

 

posted @ 2019-05-13 23:42  缘未到  阅读(174)  评论(0编辑  收藏  举报