題意:有n個人,他們之間有上司和下屬關係,每個人有自己的價值,現在要選一部分人使其滿足上司和下屬不同時被選到的情況下價值總和最大。更直接的講就是,在一棵樹中選價值總和儘量多的節點但是不能同時選到一個節點和它的直接父節點。
題解:因爲這裏要求選的點的個數是不限定的,只需要滿足價值總和儘量大。而對於每個點來說,只有選和不選兩種狀態,這種情況其實和01揹包有一些類似,所以我們可以用揹包的思想來做。
定義dp[i][0]表示節點i不選,dp[i][1]表示節點要選。初始化dp[i][0]爲0(不選這個點一開始就沒有價值),dp[i][1]=a[i](選了這個點肯定它的價值就要加到總的價值中去)。那麼狀態轉移時:
不選當前這個點i:dp[i][0]+=max(dp[j][0],dp[j][1])//當前節點不選那麼它的兒子可以任意選不選。
選當前這個點i:dp[i][1]+=dp[i][0]//當前節點已經被選那麼它的兒子就不能和它同時被選。
而最終的答案由根節點的最大值決定:ans=max(dp[rt][0],dp[rt][1]);
整個過程十分簡單,在推出了狀態轉移方程以後只需要根據給的信息建樹然後在樹上dfs即可得到答案,需要注意的是爲了找出根節點一開始要記錄每個節點的入度,最後入度爲0的節點就是我們的根節點。
附上代碼:
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<math.h>
#include<vector>
using namespace std;
const int maxn=6010;
int dp[maxn][2];
int a[maxn],in[maxn],vis[maxn];
vector<int>q[maxn];
void dfs(int st)
{
vis[st]=1;
int num=q[st].size();
for(int i=0;i<num;i++)
{
int nn=q[st][i];
if(vis[nn]) continue;
dfs(nn);
dp[st][0]+=max(dp[nn][0],dp[nn][1]);
dp[st][1]+=dp[nn][0];
}
return;
}
int main()
{
int n,x,y;
while(scanf("%d",&n)!=EOF&&n){
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
memset(in,0,sizeof(in));
for(int i=1;i<=n;i++)
q[i].clear();
while(scanf("%d%d",&x,&y)!=EOF&&x+y){
q[y].push_back(x);
in[x]++;
}
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
dp[i][1]=a[i];
for(int i=1;i<=n;i++)
{
if(!in[i])//入度爲0那就是根節點
{
memset(vis,0,sizeof(vis));
dfs(i);
printf("%d\n",max(dp[i][0],dp[i][1]));
break;
}
}
}
return 0;
}