题目描述
给定一个 n个点 mm条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。
输入格式
第一行两个正整数 n,m
第二行 n个整数,依次代表点权
第三至 m+2 行,每行两个整数 u,v,表示一条 u→v 的有向边。
输出格式
共一行,最大的点权之和。
输入输出样例
输入 #1
2 2
1 1
1 2
2 1
输出 #1
2
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 100005;
int n,m;
int head[maxn],tot;
int dfn[maxn],low[maxn],Stack[maxn],sum[maxn],belong[maxn],top,ans,cnt;
int x[maxn],y[maxn],w[maxn],dis[maxn];
bool vis[maxn];
struct node
{
int to,next;
}a[maxn];
void add(int u,int v)
{
a[tot].to = v;
a[tot].next = head[u];
head[u] = tot++;
}
void tarjan(int u)
{
int v;
low[u] = dfn[u] = ++cnt;
vis[u] = true;
Stack[top++] = u;
for(int i=head[u]; i != -1; i=a[i].next)
{
v = a[i].to;
if(!dfn[v])
{
tarjan(v);
low[u] = min(low[u],low[v]);
}
else
{
if(vis[v])
low[u] = min(low[u],low[v]);
}
}
if(low[u] == dfn[u])
{
ans++;
do
{
v = Stack[--top];
vis[v] = false;
belong[v] = ans;
sum[ans] += w[v];
}
while(v != u);
}
}
void solve()
{
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(vis,false,sizeof(vis));
top = cnt = ans = 0;
for(int i=1; i<=n; i++)
{
if(!dfn[i])
tarjan(i);
}
}
void dp(int cur)
{
dis[cur] = sum[cur];
int mx = 0;
for(int i=head[cur]; i != -1; i=a[i].next)
{
int v = a[i].to;
if(!dis[v])
{
dp(v);
}
mx = max(mx,dis[v]);
}
dis[cur] += mx;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++)
{
scanf("%d",&w[i]);
}
memset(head,-1,sizeof(head));
tot = 0;
for(int i=1; i<=m; i++)
{
scanf("%d%d",&x[i],&y[i]);
add(x[i],y[i]);
}
solve();
memset(a,0,sizeof(a));
memset(head,-1,sizeof(head));
tot = 0;
for(int i=1; i<=m; i++)//把同一联通分量的缩成同一个点,然后不同连通分量之间连起来
{
if(belong[x[i]] != belong[y[i]])
add(belong[x[i]],belong[y[i]]);
}
int num = 0;
for(int i=1; i<=ans; i++)
{
if(!dis[i])
{
dp(i);
num = max(num,dis[i]);
}
}
printf("%d",num);
}
来源:CSDN
作者:菜得抠jio的茵仔
链接:https://blog.csdn.net/weixin_43678350/article/details/103846834