题意
给定一个 (n) 个点 (m) 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次
Solution
先缩点,就成了一个DAG图,做一遍拓扑排序,按拓扑序进行DP。
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 10010,M = 100010 * 2;
int head[N],ver[M],nxt[M],hc[N],vc[M],nc[M],tot,tc;
int ins[N],low[N],s[N],dfn[N],c[N],cnt,num,top,sum[N];
vector<int> scc[N];
void add(int x,int y){
nxt[++tot] = head[x];
ver[tot] = y;
head[x] = tot;
}
void addc(int x,int y){
nc[++tc] = hc[x];
vc[tc] = y;
hc[x] = tc;
}
void tarjan(int x){
low[x] = dfn[x] = ++num;
s[++top] = x; ins[x] = 1;
for(int i = head[x]; i; i = nxt[i]){
int v = ver[i];
if(!dfn[v]){
tarjan(v);
low[x] = min(low[x],low[v]);
}
else if(ins[v])
low[x] = min(low[x],dfn[v]);
}
if(low[x] == dfn[x]){
cnt++; int y;
do{
y = s[top--]; ins[y] = 0;
scc[cnt].push_back(y);
c[y] = cnt;
}while(x != y);
}
}
int n,m,a[N],deg[N],dp[N];
void dfs(int x){
dp[x] = sum[x];
for(int i = hc[x]; i; i = nc[i]){
int v = vc[i];
dfs(v);
dp[x] = max(dp[x],dp[v] + sum[x]);
}
}
int topo(){
queue<int> q;
for(int i = 1; i <= cnt; i++)
if(!deg[i]) {
q.push(i);
dp[i] = sum[i];
}
while(!q.empty()){
int x = q.front(); q.pop();
for(int i = hc[x]; i; i = nc[i]){
int y = vc[i];
dp[y] = max(dp[y],dp[x] + sum[y]);
deg[y]--;
if(!deg[y])
q.push(y);
}
}
int ans = 0;
for(int i = 1; i <= cnt; i++)
ans = max(ans,dp[i]);
return ans;
}
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i++)
cin >> a[i];
for(int i = 1; i <= m; i++){
int u,v;
cin >> u >> v;
add(u,v);
}
for(int i = 1; i <= n; i++)
if(!dfn[i]) tarjan(i);
for(int x = 1; x <= n; x++){
for(int i = head[x]; i; i = nxt[i]){
int v = ver[i];
if(c[x] == c[v]) continue;
addc(c[x],c[v]);
deg[c[v]]++;
}
}
for(int i = 1; i <= cnt; i++){
for(int j = 0; j < scc[i].size(); j++)
sum[i] += a[scc[i][j]];
}
cout << topo() << endl;
return 0;
}