@Gary-Ying
2018-08-01T14:57:11.000000Z
字数 2181
阅读 1145
解题报告
给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。
第一行,n,m
第二行,n个整数,依次代表点权
第三至m+2行,每行两个整数u,v,表示u->v有一条有向边
共一行,最大的点权之和。
2 21 11 22 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、
for (int i = 1; i <= n; ++i)if (!dfn[i])tarjan(i);
3、 第84行:addedge1(color[i],color[v]);
#include<iostream>#include<cstdio>using namespace std;const int maxn = 10007;const int maxm = 100007;int n, m;int c[maxn], a[maxn];int edgenum, head[maxn], Next[maxm], vet[maxm];int edgenum1, head1[maxn], Next1[maxm], vet1[maxm];int dfn[maxn], low[maxn], stamp;int stack[maxn], top, color[maxn], num;int init[maxn];bool vis[maxn];void addedge(int u, int v){++edgenum;vet[edgenum] = v;Next[edgenum] = head[u];head[u] = edgenum;}void addedge1(int u, int v){++edgenum1;vet1[edgenum1] = v;Next1[edgenum1] = head1[u];head1[u] = edgenum1;++init[v];}void tarjan(int u){dfn[u] = low[u] = ++stamp;stack[++top] = u; vis[u] = true;for (int e = head[u]; e; e = Next[e]){int v = vet[e];if (!dfn[v]){tarjan(v);low[u] = min(low[u], low[v]);}else if (vis[v])low[u] = min(low[u], dfn[v]);}if (dfn[u] == low[u]){color[u] = ++num; vis[u] = false;while (stack[top] != u) vis[stack[top]] = false, color[stack[top--]] = num;--top;}}void tp(){int head = 0, tail = -1, que[maxn], dp[maxn];for (int i = 1; i <= num; ++i) {if (init[i] == 0){que[++tail] = i;}dp[i] = a[i];}while (head <= tail){int u = que[head]; ++head;for (int e = head1[u]; e; e = Next1[e]){int v = vet1[e];--init[v];if (init[v] == 0) que[++tail] = v;dp[v] = max(dp[v], dp[u] + a[v]);}}int ans = 0;for (int i = 1; i <= num; ++i)if (dp[i] > ans) ans = dp[i];printf("%d\n", ans);}int main(){scanf("%d%d", &n, &m);for (int i = 1; i <= n; ++i)scanf("%d", &c[i]);for (int i = 1; i <= m; ++i){int u, v;scanf("%d%d", &u, &v);addedge(u,v);}for (int i = 1; i <= n; ++i)if (!dfn[i])tarjan(i);for (int i = 1; i <= n; ++i){for (int e = head[i]; e; e = Next[e]){int v = vet[e];if (color[i]!=color[v]){addedge1(color[i],color[v]);}}a[color[i]] += c[i];}tp();return 0;}