HDOJ-4276(树形DP+背包DP)

题目:http://acm.hdu.edu.cn/showproblem.php?pid=4276

好久没有1A过树形DP了,真是开森呢~~~

因为结构是树,所以从1到N肯定会经过最短路径,冗余的时间才能去访问别的节点(并返回最短路径上的节点!!!),因此我选择将这条必然经过的最短路径分离出来,对每个关键节点进行属性DP,再对这条关键路径进行背包DP,具体分析见代码注释。

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
#define MAX_NODE	111
#define MAX_EDGE	222
#define MAX_TIME	555
/*
  (1)find critial path from 1 to N, it forms an array path[], 
  	 with length being M. this can be solved by BFS.
  (2)we can spend E extra time apart from those on the critial path,
     so we can find out how much more we can get if we spend 
     0~E time on each critical nodes' subtree and return to it, 
     say f[i][j] means value we can get if we spend j time on
     i's subtree and return to i. this is TreeDP with Backpack.
  (3)now how should we spend E extra time ? this is BackpackDP.
  	 say dp(i, j) means the max value we can get if we spend 
  	 j time on subtrees of path[i,M), then
  	 dp(i, j) = max{f[path[i]][k] + dp(i+1, j-k)}, 0 <= k <= j
*/

int N, T;
int value[MAX_NODE];		//value of each node
struct Edge{
	int to, len, next;
} edge[MAX_EDGE];
int cnt, pre[MAX_NODE];
inline void addEdge(int x, int y, int d)
{
	edge[cnt].to = y;
	edge[cnt].len = d;
	edge[cnt].next = pre[x];
	pre[x] = cnt++;
}

int dis[MAX_NODE];			//dis[i] = shortest distance from i to N
int nex[MAX_NODE];			//nex[i] is i's successive node from i to N
int path[MAX_NODE], M, PV;	//critial path, its length and its value
bool vis[MAX_NODE];			//visited flag
queue<int> Q;				//bfs queue

int f[MAX_NODE][MAX_TIME];	//f[i][j] = max value we can get by spending j time 
							//on subtree of i and return to i except critical path
int g[MAX_NODE][MAX_TIME];	//g[i][j] = max value we can get by spending j time
							//on subtrees of path[i],path[i+1],...,path[M-1]
bool init()
{
	if(2 != scanf("%d%d", &N, &T)) return false;
	cnt = 0;
	memset(pre+1, -1, N << 2);

	int i, x, y, d;
	for(i = 1; i < N; ++i){
		scanf("%d%d%d", &x, &y, &d);
		addEdge(x, y, d);
		addEdge(y, x, d);
	}
	for(i = 1; i <= N; ++i) scanf("%d", value + i);

	for(int i = 1; i <= N; ++i) memset(f[i], 0, (T+1) << 2);
	for(int i = 0; i < N; ++i) memset(g[i], -1, (T+1) << 2);
	return true;
}
void findPath()
{
	memset(vis + 1, false, N);

	dis[N] = 0;
	Q.push(N);
	vis[N] = true;
	nex[N] = -1;

	while(!Q.empty()){
		int x = Q.front(); Q.pop();
		for(int i = pre[x]; i != -1; i = edge[i].next){
			int y = edge[i].to;
			if(vis[y]) continue;
			nex[y] = x;
			dis[y] = dis[x] + edge[i].len;
			vis[y] = true;
			Q.push(y);
		}
	}

	memset(vis + 1, false, N);
	M = PV = 0;
	for(int x = 1; x != -1; x = nex[x]){
//		printf("%d ", x);
		path[M++] = x;
		PV += value[x];
		vis[x] = true;
	}
//	printf("\nPath value = %d\n", PV);
}
void treeDP(int x, int t)
{
	//if vis[x] then x is in critical path, do not count its value here
	if(!vis[x])	for(int i = 0; i <= t; ++i) f[x][i] = value[x];
	vis[x] = true;

	for(int i = pre[x]; i != -1; i = edge[i].next){
		int y = edge[i].to, d = edge[i].len << 1;
		if(vis[y] || t < d) continue;

		treeDP(y, t - d);
		for(int j = t; j >= d; --j)//total time
			for(int k = 0; k+d <= j; ++k)//spend k time in subtree y
				f[x][j] = max(f[x][j], f[x][j-d-k] + f[y][k]);
	}
//	for(int i = 0; i <= t; ++i) printf("f[%d][%d] = %d\n", x, i, f[x][i]);
}
int dp(int i, int t)
{
	if(i == M) return 0;
	if(g[i][t] != -1) return g[i][t];

	int ans = 0, x = path[i];
	for(int j = 0; j <= t; ++j){
		ans = max(ans, f[x][j] + dp(i+1, t-j));
	}
//	printf("g[%d][%d] = %d\n", i, t, ans);
	return g[i][t] = ans;
}
void solve()
{
	findPath();
	if(dis[1] > T){
		puts("0");
		return;
	}

	int surplus = T - dis[1];
	for(int i = 0; i < M; ++i) treeDP(path[i], surplus);
	printf("%d\n", dp(0, surplus) + PV);
}

int main()
{
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);

	while(init()) solve();
	return 0;	
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值