wikioi p1380 没有上司的舞会

第一次发个有点难度的。

首先根据题目的描述可以知道,这个是一个树,有这样一个关系,如果选择了某一个节点,那么就不能够选择他的孩子节点,但是不代表不选择他的子孙。

每一个节点都有一个价值,请问如何选择这棵树的节点使得他们的价值总和最大。

那么我们可以建立f[i][0]和f[i][1]分别表示选择第i个节点或者不选择,他和他的子树最大的价值总和。

如果不选择,则f[i][0]=Σf[x][1] (x是i的子节点)

如果选择,那么它的子节点就不能选择f[i][1]=w[i]+Σf[x][0]

如果该节点x为叶子节点,那么f[x][0]=0 f[x][1]=w[x]

这样就建立了一个关系式,那么经过记忆化搜索就能够完成计算,因为有一下关系f[i][0]>f[x][0] f[i][1]>f[x][1](当i为x的祖先)

并且有f[i][1]>f[i][0] 那么整个树的最大值为f[root][1]

如果不是记忆化会爆一个点。

如果加了优化速度也会快很多。

当然也可以使用DP,但是写起来会麻烦一点,初始化什么的比较难处理。

#include<stdio.h>
#include<iostream>
#include<vector>
#include<memory.h>
using namespace std;
const int MAX_N = 6001;
const int MAX_M = 2;
const int INF = -MAX_N*MAX_N;
vector<int> v[MAX_N];
int N;
int f[MAX_N][MAX_M];
bool visited [MAX_N][MAX_M];
int w[MAX_N];
bool isroot[MAX_N];
int root;
int ans = INF;
int find_root()
{
	int i;
	for (i=1;i<=N;i++)
	if (isroot[i]) return i;
	return -MAX_M;
}
int init()
{
	int i;
	int K,L;
	memset(isroot,true,sizeof(isroot));
	scanf("%d",&N);
	for (i=1;i<=N;i++)
	scanf("%d",&w[i]);
    for (i=1;i<=N;i++)
	scanf("%d %d",&K,&L),v[L].push_back(K),isroot[K]=false;
	root=find_root();
	memset(visited,false,sizeof(visited));
}
int work(int t,bool treasure)
{
    if (!v[t].size()&&treasure) return f[t][treasure]=max(0,w[t]);
	if (!v[t].size()&&!treasure) return f[t][treasure]=0;
	if (visited[t][treasure]) return f[t][treasure];
	visited[t][treasure] =  true ;
	
	int i;
	int tmp;
	f[t][treasure]=INF;
	if (treasure)
	{
		tmp = max(0,w[t]);
		for (i=0;i<v[t].size();i++)
		tmp +=max(work(v[t][i],false),0);
        f[t][treasure] = tmp ;
	}
    
	{
		tmp = 0;
		for (i=0;i<v[t].size();i++)
		tmp+=max(work(v[t][i],true),0) ;
	}
	//printf("f(%d,%d):%d\n",t,treasure,tmp);
	return f[t][treasure] = max(f[t][treasure],tmp) ;
}
int put()
{
	ans=max(f[root][true],f[root][false]);
	printf("%d",ans);
}
int main()
{
	init();
	work(root,true);
	work(root,false);
	put();
	return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值