题意:有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<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=6006;
queue<int>q;
vector<int>v[maxn];
int val[maxn];
int dp[maxn][2];
int fa[maxn];
void dfs(int rt){ //对rt的儿子节点遍历
for(int i=0;i<v[rt].size ();i++){
int temp=v[rt][i]; //temp为rt的儿子节点
dfs(temp); //dfs
dp[rt][0]+=max(dp[temp][0],dp[temp][1]); //不选的话,子节点为选或不选
dp[rt][1]+=dp[temp][0]; //选的话,子节点不能选
}
}
int main(){
int n;
while(~scanf("%d",&n)){
for(int i=1;i<=n;i++){
v[i].clear ();
scanf("%d",&val[i]); // 输入1-n权值
fa[i]=-1; //有权值标记为-1
dp[i][1]=val[i]; // 选i
dp[i][0]=0; //不选i
}
int a,b;
while(~scanf("%d%d",&a,&b),(a+b)){
fa[a]=b; //b->a
v[b].push_back (a);
}
for(int i=1;i<=n;i++){
if(fa[i]==-1){ //如果值存在
dfs(i);
printf("%d\n",max(dp[i][0],dp[i][1]));
break;
}
}
}
}