题目描述
给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。
tarjan简介
强连通分量:有向图中,若任意两节点均能通过若干有向边达到对面,则这个有向图所有节点为强连通分量。例子:环。
tarjan算法是用来求强连通分量的算法。该算法把所有经过的节点压入一个栈中,若到达的节点已经在栈中则说明已经发现了强连通分量。
一个点本身是一个强连通分量。
tarjan写法
出现的数组有:
dfn:到达某点的时间,用tim维护,每次遍历时++tim
stack:使用的栈,栈顶为top
vis:记录某点是否在栈中
low:强连通分量中最早到达的节点的次序号
belong:记录节点属于的强连通分量的编号
tarjan过程:
Tarjan(x) {
更新vis,dfn等信息
遍历每个用边连接的节点nxt {
若nxt仍未被访问(即dfn[nxt]==0) {
递归tarjan(nxt)
更新low信息
} else {
若nxt在栈中(即vis[nxt]) {
更新low信息
}
}
}
若x是所在强连通分量中的第一个节点 {
处理强连通分量中vis信息
处理强连通分量中belong信息
把强连通分量中其他节点的点权加到x中(准备缩点)
}
}
缩点写法
遍历之前的所有边,找到两端点的强连通分量编号(belong值),在两强连通分量之间加边。
代码
剩下用拓扑排序+DP实现求距离即可。
代码:
#include<stdio.h>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')
{
x=10*x+ch-'0';
ch=getchar();
}
return x*f;
}
const int Size=10005;
int n,m,p[Size];
struct Edge
{
int u,v,next;
} w[100001]; //链式前向星存边,u连到v
int cnt,head[Size];
void AddEdge(int u,int v) //加边
{
cnt++;
w[cnt].u=u;
w[cnt].v=v;
w[cnt].next=head[u];
head[u]=cnt;
}
//tarjan算法中用到的各数组
int tim,top,dfn[Size],stack[Size],belong[Size],low[Size];
bool vis[Size];
void tarjan(int x)
{
vis[x]=true;
low[x]=dfn[x]=++tim;
stack[++top]=x;
for(int i=head[x]; i; i=w[i].next)
{
int nxt=w[i].v;
if(!dfn[nxt])
{
tarjan(nxt); //递归对nxt求tarjan
low[x]=min(low[x],low[nxt]);
} else if(vis[nxt]) {
low[x]=min(low[x],low[nxt]);
}
}
if(dfn[x]==low[x])
{
int lst=stack[top];
while(top--)
{
belong[lst]=x;
vis[lst]=false;
if(lst==x)
{
return;
}
p[x]+=p[lst];
lst=stack[top];
}
}
}
int indegree[Size]; //节点入度
void init() //预处理输入
{
n=read();
m=read();
for(int i=1; i<=n; i++)
{
p[i]=read();
}
for(int i=1; i<=m; i++)
{
int u=read();
int v=read();
AddEdge(u,v);
}
}
void Shrink() //缩点
{
for(int i=1; i<=n; i++)
{
if(!dfn[i])
{
tarjan(i);
}
}
memset(head,0,sizeof(head));
cnt=0;
for(int i=1; i<=m; i++)
{
int b1=belong[w[i].u];
int b2=belong[w[i].v];
if(b1!=b2)
{
AddEdge(b1,b2);
indegree[b2]++;
}
}
}
int Queue[Size],dist[Size];
void TopoSort() //拓扑排序+DP
{
// memset(dist,0x3f,sizeof(dist));
int hd=0,tl=0;
for(int i=1; i<=n; i++)
{
if(belong[i]==i && !indegree[i])
{
Queue[++tl]=i;
dist[i]=p[i];
}
}
while(hd<=tl)
{
int x=Queue[++hd];
for(int i=head[x]; i; i=w[i].next)
{
int nxt=w[i].v;
indegree[nxt]--;
dist[nxt]=max(dist[nxt],dist[x]+p[nxt]);
// printf("v=%d dist[v]=%d\n",nxt,dist[nxt]);
if(!indegree[nxt])
{
Queue[++tl]=nxt;
}
}
}
}
int main()
{
init();
Shrink();
TopoSort();
int ans=0;
for(int i=1; i<=n; i++)
{
if(dist[i]>ans)
{
ans=dist[i];
}
}
printf("%d\n",ans);
return 0;
}