Description
给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。
Input
第一行,n,m
第二行,n个整数,依次代表点权
第三至m+2行,每行两个整数u,v,表示u->v有一条有向边
Output
共一行,最大的点权之和。
缩点+DP这是题目说的
先缩点,对于每一个联通块之间建边,这时得到一张(DAG)(有向无环图)
我们对其跑拓扑排序,然后开一个数组(dis)记录到达某个点的最大值.
对于那些入度为0的点,我们初始化其(dis)为其联通块的点权之和.
然后每次取(max)即可.
最终(ans)即为对到达每个点的(dis)取(max)。
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#define R register
using namespace std;
const int gz=50008;
inline void in(int &x)
{
int f=1;x=0;char s=getchar();
while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
while(isdigit(s)){x=x*10+s-'0';s=getchar();}
x*=f;
}
int head[gz],tot,val[gz],v[gz],h[gz],dis[gz],ins[gz],ans,n,m;
struct cod{int u,v;}edge[gz<<1],e[gz<<1];
inline void add(R int x,R int y)
{
edge[++tot].u=head[x];
edge[tot].v=y;
head[x]=tot;
}
inline void ado(R int x,R int y)
{
e[++tot].u=h[x];
e[tot].v=y;
h[x]=tot;
}
int dfn[gz],belong[gz],idx,low[gz],stk[gz],top,col;
bool inq[gz];
void tarjan(R int x)
{
low[x]=dfn[x]=++idx;
stk[++top]=x;inq[x]=true;
for(R int i=head[x];i;i=edge[i].u)
{
if(!dfn[edge[i].v])
{
tarjan(edge[i].v);
low[x]=min(low[x],low[edge[i].v]);
}
else if(inq[edge[i].v])
low[x]=min(low[x],dfn[edge[i].v]);
}
if(low[x]==dfn[x])
{
int now=-1;
col++;
while(now!=x)
{
now=stk[top--];
belong[now]=col;
inq[now]=false;
v[col]+=val[now];
}
}
}
inline void topsort()
{
top=0;
for(R int i=1;i<=col;i++)
if(!ins[i])stk[++top]=i,dis[i]=v[i];
while(top)
{
int u=stk[top--];
for(R int i=h[u];i;i=e[i].u)
{
ins[e[i].v]--;
dis[e[i].v]=max(dis[e[i].v],dis[u]+v[e[i].v]);
if(!ins[e[i].v])stk[++top]=e[i].v;
}
}
for(R int i=1;i<=col;i++)
ans=max(ans,dis[i]);
printf("%d
",ans);
}
int main()
{
in(n),in(m);
for(R int i=1;i<=n;i++)in(val[i]);
for(R int i=1,x,y;i<=m;i++)
{
in(x),in(y);
add(x,y);
}
for(R int i=1;i<=n;i++)
if(!dfn[i])tarjan(i);
tot=0;
for(R int i=1;i<=n;i++)
for(R int j=head[i];j;j=edge[j].u)
if(belong[i]!=belong[edge[j].v])
{
ins[belong[edge[j].v]]++;
ado(belong[i],belong[edge[j].v]);
}
topsort();
}