HDU 4276 The Ghost Blows Light(12年长春 树形DP+spfa)

转载请注明出处,谢谢http://blog.csdn.net/acm_cxlove/article/details/7854526       by---cxlove 

题目:给出一棵树,从1走到n,总时间为T,每走一条边需要花费一定时间,每个结点有一定价值,问在指定时间内回到T的能获取的最大价值

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

比赛的时候是yobobobo出的这题。

不知道怎么处理回到n点,果然DP不是我的菜。

首先spfa求一次最短路,如果从1到n的最短花费都小于总时间,则是不可能。

而且在最短路的时候记录每个节点的父节点,以及边的编号

然后将最短路径上的所有边权置为0.将总时间减去最短路径。

这样处理的目的是,显然最短路上的边只会走一遍才是最优的,而且剩下的边要么不走,要么走两次,而且DFS求一次树形DP之后,不需要考虑回到n的情况,因为1-n的最短路径走了一遍,其它边走0次或两次,肯定是回到n的。

这样就成了树上的分组背包了~~~囧,

树形DP很弱,手很生

#include<iostream>
#include<cstdio>
#include<map>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
#include<set>
#include<queue>
#define inf 1<<27
#define N 105
#define M 505
#define Min(a,b) ((a)<(b)?(a):(b))
#define Max(a,b) ((a)>(b)?(a):(b))
#define pb(a) push_back(a)
#define LL long long
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
struct Node{
	int v,w,next;
}edge[N*2];
int tot,start[N],val[N],dist[N];
int n,t,dp[N][M],pre[N],p[N];
void _addedge(int u,int v,int w){
	edge[tot].v=v;edge[tot].w=w;
	edge[tot].next=start[u];
	start[u]=tot++;
}
void addedge(int u,int v,int w){
	_addedge(u,v,w);
	_addedge(v,u,w);
}
void spfa(int s){
	queue<int>que;
	int vis[N];
	mem(vis,0);mem(pre,0);
	for(int i=1;i<=n;i++)
		if(i==s)
			dist[i]=0;
		else
			dist[i]=inf;
	que.push(s);
	vis[s]=1;
	while(!que.empty()){
		int u=que.front();
		que.pop();
		vis[u]=0;
		for(int i=start[u];i!=-1;i=edge[i].next){
			int v=edge[i].v,w=edge[i].w;
			if(dist[v]>dist[u]+w){
				dist[v]=dist[u]+w;
				pre[v]=u;
				p[v]=i;
				if(!vis[v]){
					vis[v]=1;
					que.push(v);
				}
			}
		}
	}
	for(int i=n;i!=1;i=pre[i]){
		edge[p[i]].w=0;
		edge[p[i]^1].w=0;
	}
}
void dfs(int u,int father){
	for(int i=start[u];i!=-1;i=edge[i].next){
		int v=edge[i].v,w=edge[i].w*2;
		if(v==father) continue;
		dfs(v,u);
		for(int i=t;i>=w;i--)
			for(int j=i-w;j>=0;j--)
				dp[u][i]=max(dp[u][i],dp[u][j]+dp[v][i-j-w]);
	}
	for(int i=0;i<=t;i++)
		dp[u][i]+=val[u];
}
int main(){	
	while(scanf("%d%d",&n,&t)!=EOF){
		tot=0;mem(start,-1);
		for(int i=1;i<n;i++){
			int u,v,w;
			scanf("%d%d%d",&u,&v,&w);
			addedge(u,v,w);
		}
		for(int i=1;i<=n;i++)
		    scanf("%d",&val[i]);
		spfa(1);
		if(dist[n]>t){
			puts("Human beings die in pursuit of wealth, and birds die in pursuit of food!");
			continue;
		}
		mem(dp,0);
		t-=dist[n];
		dfs(1,0);
		printf("%d\n",dp[1][t]);
	}
	return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值