题目链接:
http://poj.org/problem?id=3160
题目大意:
一张有向图,有n个点,m条边,输入“i——j”表示可以从i点走到j点,是单向的。每个点都有权值。
你可以任意选定一个点作为起点,去遍历这个图,当经过一个点的时候,你可以选择取或者不取该点的权值(因为权值有负值)。
求能获得最大权值。
解题思路:
如果是一张有向无环图的话,那么就很简单做树形dp即可。
所以第1步应该是把图化成无环图。
第1步:求强连通分量+缩点,然后建立新图。
第2步:在新图上做树形dp,得到最大的max。
源代码:
#include<stdio.h> #include<iostream> #include<math.h> #include<string.h> #include<string> #include<map> #include<vector> #include<set> #include<queue> #include<algorithm> using namespace std; vector<int> child[30001]; vector<int> pre[30001]; int v[30001]; //每个点所拥有的权值 int n,m; int dp[30001]; //用来记录每个点所能获得的最大权值 struct node { int id; int end_time; //结束时间戳 }s[30001]; bool cmp(node a,node b) //按照时间戳进行排序 { return a.end_time>b.end_time; } int vis[30001]; int Set[30001]; //每个点属于的集合 struct node1 //新图的结构体 { int v; vector<int> child; }p[30001]; int Time; //时间戳 int cnt; //强连通分量的个数 int Max; //结果的最大值 void dfs1(int now) //dfs1求取时间戳 { int i,j,len,kid; len=child[now].size(); for(i=0;i<len;i++) { kid=child[now][i]; if(!vis[kid]) { vis[kid]=1; dfs1(kid); } } vis[now]=1; s[now].id=now; s[now].end_time=++Time; return; } void dfs2(int now) //dfs2求取强连通分量 { int i,j,len,father; len=pre[now].size(); for(i=0;i<len;i++) { father=pre[now][i]; if(!vis[father]) { vis[father]=1; dfs2(father); } } Set[now]=cnt; if(v[now]>0) p[cnt].v+=v[now]; //顺便求取每个强连通点的权值 return; } void dfs3(int now) //树形dp求取当前节点能获得的最大价值 { int i,j,k,len,kid; len=p[now].child.size(); if(len==0) { dp[now]=p[now].v; return; } for(i=0;i<len;i++) { kid=p[now].child[i]; if(!vis[kid]) { vis[kid]=1; dfs3(kid); } dp[now]=max(dp[now],p[now].v+dp[kid]); //当前节点的最大值等于孩子节点的最大值+本身 } return; } int main() { freopen("in.txt","r",stdin); int i,j,k,t,len,a,b,id,x,y; while(scanf("%d%d",&n,&m)==2) { memset(v,0,sizeof(v)); for(i=1;i<=n;i++) //点默认从1开始 scanf("%d",&v[i]); for(i=0;i<=n;i++) { child[i].clear(); pre[i].clear(); } while(m--) { scanf("%d%d",&a,&b); a++; b++; child[a].push_back(b); pre[b].push_back(a); } //进行dfs1求取时间戳 memset(vis,0,sizeof(vis)); Time=0; for(i=1;i<=n;i++) { if(!vis[i]) { vis[i]=1; dfs1(i); } } //按照时间戳进行排序 sort(s+1,s+1+n,cmp); //按照排序后的点,进行第dfs2 memset(vis,0,sizeof(vis)); memset(p,0,sizeof(p)); cnt=0; for(i=1;i<=n;i++) { id=s[i].id; if(!vis[id]) { cnt++; vis[id]=1; dfs2(id); } } //建立新图 for(i=1;i<=n;i++) { x=Set[i]; len=child[i].size(); for(j=0;j<len;j++) { y=child[i][j]; y=Set[y]; if(x==y) continue; p[y].child.push_back(x); //y是x的孩子节点 //p[x].du++; //统计出度 } } //对新图进行树形dp Max=0; memset(vis,0,sizeof(vis)); memset(dp,0,sizeof(dp)); for(i=1;i<=cnt;i++) { if(!vis[i]) { vis[i]=1; dfs3(i); } Max=max(dp[i],Max); } printf("%d\n",Max); } return 0; }