这道题的大意就是,给出一个有向图,每个点有一个点权,点权可能是正也可能为负,一个人从某点出发,沿着一些路,访问结点,或者仅仅是路过这个结点,而不去访问,最后求他能访问到的最大的点权和。
我们注意到,他对某个结点是可以选择访问或者不访问的,那么只用访问那些点权为正数的点了。
首先,求强连通分量,缩点,然后新点的点权就是原强连通分量中,所有正点权之和。
之后就要进行DP求解,我在本题中使用了Kosaraju算法,其好处就是,最后缩点后,恰好形成拓扑序列,而DP就要从拓扑序靠后的点往前进行DP,这样才能从叶子结点往根部进行DP,从而得到正确答案
/* ID: sdj22251 PROG: subset LANG: C++ */ #include <iostream> #include <vector> #include <list> #include <map> #include <set> #include <deque> #include <queue> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <cctype> #include <string> #include <cstring> #include <cmath> #include <ctime> #define LOCA #define MAXN 5005 #define INF 100000000 #define eps 1e-7 using namespace std; struct Edge { int v, next; }edge[10 * MAXN], revedge[10 * MAXN], newedge[MAXN]; int head[MAXN], revhead[MAXN], e, visited[MAXN], newhead[MAXN]; int order[MAXN], cnt, id[MAXN], val[MAXN], newval[MAXN]; int uu[5 * MAXN], vv[5 * MAXN], out[MAXN], dp[MAXN]; int n, m; int ans; void init() { e = 0; memset(head, -1, sizeof(head)); memset(revhead, -1, sizeof(revhead)); memset(newhead, -1, sizeof(newhead)); memset(out, 0 , sizeof(out)); memset(newval, 0, sizeof(newval)); memset(dp, 0, sizeof(dp)); } void insert(const int &x, const int &y) { edge[e].v = y; edge[e].next = head[x]; head[x] = e; revedge[e].v = x; revedge[e].next = revhead[y]; revhead[y] = e; e++; } void newinsert(const int &x, const int &y) { newedge[e].v = y; newedge[e].next = newhead[x]; newhead[x] = e++; } void readdata() { for(int i = 1; i <= n; i++) scanf("%d", &val[i]); for(int i = 0; i < m; i++) { scanf("%d%d", &uu[i], &vv[i]); uu[i]++; vv[i]++; insert(uu[i], vv[i]); } } void dfs(int u) { visited[u] = 1; for(int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].v; if(!visited[v]) dfs(v); } order[cnt++] = u; } void dfs_rev(int u) { visited[u] = 1; id[u] = cnt; if(val[u] > 0) newval[cnt] += val[u]; for(int i = revhead[u]; i != -1; i = revedge[i].next) { int v = revedge[i].v; if(!visited[v]) dfs_rev(v); } } void Kosaraju() { init(); readdata(); memset(visited, 0, sizeof(visited)); cnt = 0; for(int i = 1; i <= n; i++) { if(!visited[i]) dfs(i); } memset(visited, 0, sizeof(visited)); cnt = 0; for(int i = n - 1; i >= 0; i--) { if(!visited[order[i]]) { cnt++; dfs_rev(order[i]); } } e = 0; for(int i = 0; i < m; i++) { int u = id[uu[i]]; int v = id[vv[i]]; if(u != v) newinsert(u, v); } ans = 0; for(int u = cnt; u >= 1; u--) { int tmp = 0; dp[u] = newval[u]; for(int i = newhead[u]; i != -1; i = newedge[i].next) { int v = newedge[i].v; tmp = max(tmp, dp[v]); } dp[u] += tmp; ans = max(ans, dp[u]); } } int main() { while(scanf("%d%d", &n, &m) != EOF) { Kosaraju(); printf("%d\n", ans); } return 0; }