題目鏈接:
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;
}