题目描述
给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。
输入输出格式
输入格式:
第一行,n,m
第二行,n个整数,依次代表点权
第三至m+2行,每行两个整数u,v,表示u->v有一条有向边
输出格式:
共一行,最大的点权之和。
输入输出样例
输入样例#1:
2 2
1 1
1 2
2 1
输出样例#1:
2
说明
n<=10^4
m<=10^5
0<=点权<=1000
【解题思路】:
算法:Tarjan缩点+DAGdp
【AC代码】:
#include<bits/stdc++.h>
#define M(a,b) memset(a,b,sizeof(a))
#define pi 3.1415926
using namespace std;
inline void read(int &x){
char ch=getchar(),c=ch;
x=0;
while(ch<'0' || ch>'9'){
c=ch;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
if(c=='-')x=-x;
}
int n,m,i,idx,cnt;
int dfn[10010],low[10010];
int in[10010],v[10010],fa[10010];
int head[10010];
bool used[10010];
int stk[10010],p;
int ans=0;
struct Node{
int nw,nxt,mark;
}edge[100010];
void add(int x,int y,int cnt){
edge[cnt].nw=x;
edge[cnt].mark=head[x];
edge[cnt].nxt=y;
head[x]=cnt;
}
void tarjan (int u){
dfn[u]=low[u]=++idx;
stk[++p]=u;
used[u]=1;
for(int i=head[u];i!=0;i=edge[i].mark){
int nx=edge[i].nxt;
if(!dfn[nx]){
tarjan (nx);
low[u]=min(low[u],low[nx]);
}
else if(used[nx])low[u]=min(low[u],dfn[nx]);
}
if(low[u]==dfn[u]){
while(stk[p+1]!=u){
v[u]+=v[stk[p]];
fa[stk[p]]=u;
used[stk[p]]=0;
p--;
}
v[u]>>=1;
}
}
int topo(){
int i,dis[10010];
queue<int>q;
for(i=1;i<=n;i++){
if(fa[i]==i){
dis[i]=v[i];
if(!in[i])q.push(i);
}
}
while(!q.empty()){
int Now=q.front();
for(i=head[Now];i!=0;i=edge[i].mark){
int Nxt=edge[i].nxt;
dis[Nxt]=max(dis[Nxt],dis[Now]+v[Nxt]);
in[Nxt]--;
if (!in[Nxt])q.push (Nxt);
}
q.pop();
}
int maxx=0;
for(i=1;i<=n;i++)if(fa[i]==i)maxx=max (maxx,dis[i]);
return maxx;
}
int main(){
M(in,0);
M(used,0);
M(dfn,0);
M(head,0);
read(n),read(m);
for(i=1;i<=n;i++)read(v[i]);
for(i=1;i<=m;i++){
int a,b;
read(a),read(b);
add(a,b,i);
}
for(i=1;i<=n;i++)if(!dfn[i])tarjan (i);
M(head,0);
for(i=1;i<=m;i++){
if(fa[edge[i].nw]!=fa[edge[i].nxt]){
add (fa[edge[i].nw],fa[edge[i].nxt],++cnt);
in[fa[edge[i].nxt]]++;
}
}
cout<<topo()<<'\n';
return 0;
}