您的位置:首页 > 其它

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

2015-01-13 00:45 281 查看
题目: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
= 0;
Q.push(N);
vis
= true;
nex
= -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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: