树形dp

T1:http://www.gdfzoj.com/oj/contest/161/problems/9

因为宝藏拿了就没了,所以注意循环顺序

代码:

#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;

struct node
{
	int end,t;
};

const int maxSize=100;
vector <node> M[maxSize+5];
int v[maxSize+5],f[maxSize+5][maxSize+5];
int k,m,n;

void dfs(int x,int fa)
{
	int i,y,ti,k1,j;
	
	f[x][0]=v[x];
	for (i=0;i<M[x].size();i++)
	{
		y=M[x][i].end;
		ti=M[x][i].t;
		if (y!=fa)
		{
			dfs(y,x);
			for (j=m;j;j--)//j、k1要倒着循环
			{//如果正着来就会在加f[x][...]时会重复加f[y][k1],因为f[x][...]已经算了f[y][k1] 
				for (k1=j-2*ti;k1>=0;k1--) 
					f[x][j]=max(f[x][j],f[y][k1]+f[x][j-k1-2*ti]);
			}
		}
	}
	
	return ;
}

int main()
{
	int i,x,y,w,ans;
	node n1;
	
	freopen("a.txt","r",stdin);
	while (scanf("%d",&n)!=EOF)
	{
		memset(v,0,sizeof(v));
		memset(f,0,sizeof(f));
                for (i=1;i<=n;i++)
			scanf("%d",&v[i]);
		for (i=1;i<n;i++)
		{
			scanf("%d%d%d",&x,&y,&w);
			n1.end=y;	n1.t=w;
			M[x].push_back(n1);
			n1.end=x;
			M[y].push_back(n1);
		}
		scanf("%d%d",&k,&m);
		dfs(k,k);
		ans=0;
		for (i=0;i<=m;i++)
			ans=max(ans,f[k][i]);
		printf("%d\n",ans);
	}
	
	
	
	return 0;
}

T2:http://www.gdfzoj.com/oj/contest/161/problems/10

思路详见qq空间

程序方面:

1.写程序前,必须详细推出dp公式(哪种情况求和,哪种情况直接取值)

不要推到一半感觉就是这样就上了~

2.dp中为了min的maxValue不要过大(会爆)适当即可

3.对于特殊情况(叶子结点、根结点)

若dp公式中有min、max,则可以直接赋maxValue或0值(max、min时会筛掉)

可以省略特判

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxSize=10000,maxValue=1000000;
int n;
vector <int> M[maxSize+5];
int f[maxSize+5][3];

void dfs(int x,int fa)
{
	int i,y;
	
	f[x][0]=1;	f[x][2]=maxValue;
	//若是叶子结点被附最值也无妨(最后会被min排除掉)
	//这里maxValue最好不要赋值过大(适当即可,会爆) 
	for (i=0;i<M[x].size();i++)
	{
		y=M[x][i];
		if (y!=fa)
		{
			dfs(y,x);
			f[x][0]+=min(f[y][1],f[y][0]);
			//这里是+,不是直接min
			//其他子树只是有可能结点不选,但子结点选 
			f[x][1]+=f[y][2];
		}
	}
	for (i=0;i<M[x].size();i++)
	{
		y=M[x][i];
		if (y!=fa)
			f[x][2]=min(f[x][2],f[x][1]-f[y][2]+f[y][0]);
	}
}

int main()
{
	int i,x,y,ans;
	
	freopen("a.txt","r",stdin);
	while (scanf("%d",&n))
	{
		if (n==-1)
			break;
		if (n==0)
			continue;
		for (i=1;i<=n;i++)
		{
			f[i][0]=1;	f[i][2]=maxValue;
			M[i].clear();
		}
		for (i=1;i<n;i++)
		{
			scanf("%d%d",&x,&y);
			M[x].push_back(y);
			M[y].push_back(x);
		}
		dfs(1,1);
		ans=min(f[1][0],f[1][2]);
		printf("%d\n",ans);
		memset(f,0,sizeof(f));
	}
	
	
	
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值