TELE (树形DP&背包扩展) #by Plato

http://poj.org/problem?id=3017


题意:N个结点的树,有M个叶子结点,取每条边有花费Ci,取每个叶子结点有价值Pi;

求:在保证总收入(总价值-总花费)>0的情况下,能取到的叶子结点的最大数目

Idea:其实就是把背包给扩展到了树结构,通过TreeDP+分组背包来实现。


#include <cstdio>
#include <fstream>
#include <iostream>
#include <cstring>
#include <string>
using namespace std;
#define maxn(x,y) ((x)>(y)?(x):(y))

const int INF_MIN = -(1<<28);

struct node{
	int ev,next;
};
int c[3010],maxn[3010];
int N,M;
int price[3010];
node a[100000];
int fa;
int head[3010];
int f[3010][3010];

void Add_edge(int fv,int ev,int cost)
{
	fa++;
	a[fa].ev = ev;
	c[ev] = cost;
	a[fa].next = head[fv];
	head[fv] = fa;
	maxn[fv]++;
}

void TreeDp(int fv)
{
	int i,k,j,m;

	if (head[fv] == -1){
		f[fv][1] = price[fv] - c[fv];
		return;
	}

	for (i = head[fv];i != -1;i = a[i].next)
	{
		k = a[i].ev;
		TreeDp(k);
		maxn[fv] += maxn[k];
		for (j = maxn[fv];j >= 1;j--)//开始时偷懒,j的范围直接从M->1,结果TLE;后来改成了maxn[fv];其实也应该,这个循环是在递归中,以前是在main中,调用次数相差很大
		{
			for (m = 1;m <= j;m++)
			{
				f[fv][j] = maxn(f[fv][j],f[fv][j-m]+f[k][m]);
			}
		}
	}
    for (i = 1;i  <= M;i++) f[fv][i] -= c[fv];//f[fv][i] -= c[fv]; 这里也比较坑,样例都改了好久才过,思维不够严密啊
}

int main()
{
    //freopen("test.txt","r",stdin);
	int i,j,k,t1,t2;
	int K;
	while(~scanf("%d%d",&N,&M))
	{
		fa = 0;
		memset(head,-1,sizeof(head));
		memset(maxn,0,sizeof(maxn));

		c[1] = 0;
		for (i = 1;i <= N-M;i++)
		{
			scanf("%d",&K);
			for (j = 1;j <= K;j++)
			{
				scanf("%d%d",&t1,&t2);
				Add_edge(i,t1,t2);//Add_edge(j,t1,t2);
			}
		}
		for (i = N-M+1;i <= N;i++)//for (i = 1;i <= M;i++)
		{
			scanf("%d",&price[i]);
		}
		for (i = 0;i <= N;i++)
		{
			f[i][0] = 0;
			for (j = 1;j <= M;j++)
			{
				f[i][j] = INF_MIN;
			}
		}
		TreeDp(1);

		for (i = M;i >= 0;i--)
		{
			if (f[1][i] >= 0) break;
		}
		printf("%d\n",i);
	}

	return 0;
}
/*
code:
debug:1st返回WA,估计是输出代码没注释掉;2rd返回LTE,少了个比较明显的优化(主要没想到居然会卡这个优化)
*/



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值