链接
题目描述
给定一个n个点m条边的有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是重复经过的点,权值只计算一次。
样例输入
2 2
1 1
1 2
2 1
样例输出
2
思路
这题我们可以把能够互相到达的点缩成一个点,然后把点权统计起来,再与别的缩点连边
那么这个求可缩的点就是要求强连通分量
我们用Tarjan算法来实现
记录
d
f
n
i
dfn_i
dfni表示i这个点的dfs序
再设
l
o
w
i
low_i
lowi表示i所在强连通分量的dfn值最小的点,也就是这个强连通分量的代表点
最后设
c
o
l
i
col_i
coli表示i这个点所在的强连通分量的编号(也是缩点的编号
遍历到x点的时候
我们找它的所连点,然后一个个求遍历,并更新low值,若有一个点的dfn值和low值相等,则此点为所在强连通分量的代表,以此点编强连通分量的号码
以上只是简单带过
详见Tarjan讲解
缩点之后再想一下怎么求最大值
给出一个有向无环图,然后每个点有点权,我想到用拓扑排序外加DP去求最大值
代码
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
int n, m, ans, t, tt, top, p, Tt;
int h[100007], hh[100007];
int sum[100007], a[100007], ru[100007], f[100005];
int col[100005], s[100005], dfn[100005], low[100005];
struct q
{
int to, next;
}gg[1000005];
struct node
{
int from, to, next;
}g[1000005];
void addx(int u, int v)
{
gg[++tt] = (q){v, hh[u]}; hh[u] = tt;
}
void add(int u, int v)
{
g[++t] = (node){u, v, h[u]}; h[u] = t;
}
void Tarjan(int x)
{
dfn[x] = low[x] = ++Tt;
s[++top] = x;
for(int i = h[x]; i; i = g[i].next)
{
int to = g[i].to;
if(!dfn[to])
{
Tarjan(to);
low[x] = min(low[x], low[to]);
}
else if(!col[to]) low[x] = min(low[x], low[to]);
}
if(dfn[x] == low[x]) {
col[x] = ++p;
sum[p] += a[x];
while(s[top] != x)//在x后入栈的点都处在x的强连通分量中(因为是x的连边过去的)
col[s[top]] = p, sum[p] += a[s[top--]];
top--;
}
}
void dp()
{
queue<int>Q;
for(int i = 1; i <= p; ++i)
if(!ru[i]) Q.push(i), f[i] = sum[i];
while(Q.size())
{
int tot = Q.front();
Q.pop();
for(int i = hh[tot]; i; i = gg[i].next)
{
int to = gg[i].to;
f[to] = max(f[to], f[tot] + sum[to]);
ru[to]--;
if(!ru[to]) Q.push(to);
}
}
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
for(int i = 1; i <= m; ++i)
{
int u, v;
scanf("%d%d", &u, &v);
add(u, v);
}
for(int i = 1; i <= n; ++i)
if(!dfn[i]) Tarjan(i);
for(int i = 1; i <= t; ++i)
{
int to = g[i].to, from = g[i].from;
if(col[to] != col[from])//to和from所在的强连通分量不一样
{
addx(col[from], col[to]);
ru[col[to]]++;
}
}//构建新图
dp();
for(int i = 1; i <= p; ++i)
ans = max(ans, f[i]);
printf("%d", ans);
return 0;
}