题目链接

Tarjan+dp

这里的题意解释一下: 给你一个有向图,从任意一个节点出发(注意,这里只能前进不能回溯), 每一个节点都有一个权值,可正可负,可取可不取(这个负值几乎没啥意义,负数一概不取就行了),直到没有可以 可以走的节点时,权值的和最大是多少。(注意!!不能回退)

题意弄清楚之后,这道题就不难做了。
首先是用Tarjan缩点(因为缩点之后,图会变成一个森林,比较容易操作),每个缩点内的正权值全部加起来,作为这个缩点的权值。
对于一颗树来说,从根节点出发无疑是最优的,所以我们要分别从所有入度为0的点出发做dfs,用dp数组记录从该节点出发能得到的最大总权值是多少,如果下次再经过这个节点时就不需要向下dfs了
直接取节点的dp值就行了。

这里需要注意的一点是, Tarjan算法中,low[u] = min(low[u], dfn[v]) 一定要判断 v 是否在栈内。

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int MaxnN = 1e4+10;
const int MaxnE = 2e5+10;

struct Edge {
	int v, next;
} edge[MaxnE], vedge[MaxnE];

int h[MaxnN], edge_cnt;
int vh[MaxnN], vedge_cnt;
int Stack[MaxnN], Top;
int scc, indx;
int dfn[MaxnN], low[MaxnN], num[MaxnN], dp[MaxnN];
int cost[MaxnN], vcost[MaxnN], indeg[MaxnN];

void add(int u, int v) {
	edge[edge_cnt].v = v;
	edge[edge_cnt].next = h[u];
	h[u] = edge_cnt++;
}

void vadd(int u, int v) {
	vedge[vedge_cnt].v = v;
	vedge[vedge_cnt].next = vh[u];
	vh[u] = vedge_cnt++;
}

void Tarjan(int u) {
	dfn[u] = low[u] = ++indx;
	Stack[Top++] = u;
	for(int i = h[u]; i != -1; i = edge[i].next) {
		Edge e = edge[i];
		if(!dfn[e.v]) {
			Tarjan(e.v);
			low[u] = min(low[u], low[e.v]);
		} else if(!num[e.v])low[u] = min(low[u], dfn[e.v]);
	}
	if(dfn[u] == low[u]) {
		scc++;
		int v;
		do {
			v = Stack[--Top];
			num[v] = scc;
			vcost[scc] += max(0, cost[v]);
		} while(v != u);
	}
}

void dfs(int u) {
	if(dp[u]) return;
	int sum = 0;
	for(int i = vh[u]; i != -1; i = vedge[i].next) {
		Edge e = vedge[i];
		if(!dp[e.v]) dfs(e.v);
		sum = max(sum, dp[e.v]);
	}
	dp[u] = sum+vcost[u];
}

int main(void)
{
	int n, m;
	while(scanf("%d%d", &n, &m) != EOF) {
		for(int i = 0; i <= n; ++i) h[i] = -1;
		edge_cnt = 0;
		
		for(int i = 0; i < n; ++i) scanf("%d", &cost[i]);
		int u, v;
		for(int i = 1; i <= m; ++i) {
			scanf("%d%d", &u, &v);
			add(u, v);
		}
		scc = indx = Top = 0;
		for(int i = 0; i < n; ++i) dfn[i] = low[i] = vcost[i] = num[i] = 0;
		
		for(int i = 0; i < n; ++i) {
			if(!dfn[i]) Tarjan(i);
		}
		for(int i = 1; i <= scc; ++i) {
			indeg[i] = 0; vh[i] = -1;	
		}
		vedge_cnt = 0;
		for(int i = 0; i < n; ++i) {
			for(int j = h[i]; j != -1; j = edge[j].next) {
				Edge e = edge[j];
				if(num[e.v] != num[i]) {
					indeg[num[e.v]]++;
					vadd(num[i], num[e.v]);
				}
			}
		}
		for(int i = 1; i <= scc; ++i) dp[i] = 0;
		int ans = 0;
		for(int i = 1; i <= scc; ++i) {
			if(indeg[i] == 0) {
				dfs(i);
				ans = max(ans, dp[i]);
			}
		}
		printf("%d\n", ans);
	}
	return 0;
}