您的位置:首页 > 其它

uvalive 4015 洞穴cave(树的dp/01背包)

2015-05-28 23:13 148 查看
题意:一棵n(n<=500)个节点的有根树,树的边有正整数权,表示两个节点之间的距离,你的任务是回答这样的询问,从根节点出发,走不超过x(x<=5000000)单位的距离,最多能走多少个节点,节点经过多次算一个,对于每次的询问(群文次数小于1000)输出:经过节点数最大的值。 注意题目给出的 i, j,d,其中 j 是 i 的父节点。

这道题想了两天,看了题解一开始也不明白(泪目),后来一想可以当成树上的01背包来做,终于ac,这应该是目前做的最难的一道动态规划题了。

思路是:这道题的难点之一是可以返回父节点,而且由于x太大,状态里假如有x那么数组太大开不下,我们要另外选择一种方式表示状态,对此我们可以用两个状态来表示,用d[i][x][j+k][0]表示在以x结点为根结点的树中,从前i颗子树中访问j+k个结点且返回到x结点(返回用零表示)所需要的最小花费;d[i][x][j+k][1]类似,只是不用返回到x结点(用1表示),这其实就有点类似于零一背包了,但这样也有问题,数组还是太大,开不下,这时就要让数组滚动起来,用son数组记录x结点的孩子节点总数,枚举son时从大到小,这样可以把状态的第一位省掉,那么我们就可以得到状态方程了

d[x][j+k][0] = min(d[x][j + k][0], d[x][j][0] + d[t.to][k][0] + t.val * 2);//t表示x的孩子节点,val代表花费

d[x][j+k][1] = min(d[x][j + k][1], min(d[x][j][0] + d[t.to][k][1] + t.val, d[x][j][1] + d[t.to][k][0] + 2 * t.val));

#include<cstdio>  
#include<cstring>  
#include<cmath>  
#include<cstdlib>  
#include<iostream>  
#include<algorithm>  
#include<vector>  
#include<map>  
#include<queue>  
#include<stack> 
#include<string>
#include<map> 
#include<set>
using namespace std;  
#define LL long long  

const int maxn = 500 + 10;
const int maxx = 5000000 + 100;
const int INF = 0x3f3f3f3f;
int d[maxn][maxn][2], pa[maxn], son[maxn];

struct Edge {
	int from, to, val;
};

vector<int> G[maxn];
vector<Edge> edges; 

void dfs(int x) {
	d[x][1][0] = d[x][1][1] = 0;
	son[x] = 1;
	for(int i = 0; i < G[x].size(); i++) {
		Edge t = edges[G[x][i]];
		dfs(t.to);
		for(int j = son[x]; j >= 1; j--) {
			for(int k = 1; k <= son[t.to]; k++) {
				d[x][j+k][0] = min(d[x][j + k][0], d[x][j][0] + d[t.to][k][0] + t.val * 2);
				d[x][j+k][1] = min(d[x][j + k][1], min(d[x][j][0] + d[t.to][k][1] + t.val, d[x][j][1] + d[t.to][k][0] + 2 * t.val));
			}
		}
		son[x] += son[t.to];
	}
}

int main() {
	//freopen("input.txt", "r", stdin);
	int n, kase = 0;
	while(scanf("%d", &n) == 1 && n) {
		edges.clear();
		for(int i = 0; i < n; i++) G[i].clear();
		memset(pa, -1, sizeof(pa));
		memset(d, INF, sizeof(d));
		
		for(int k = 0; k < n - 1; k++) {
			int i, j, val; scanf("%d%d%d", &i, &j, &val);
			edges.push_back((Edge){ j, i, val});
			G[j].push_back(edges.size() - 1);
			pa[i] = j;
		}
		int root = 0;
		while(pa[root] != -1) root = pa[root]; 
		dfs(root);
		
		int Q; scanf("%d", &Q);
		printf("Case %d:\n", ++kase);
		while(Q--) {
			int x; scanf("%d", &x);
			int ans = 1;
			for(int i = 1; i <= n; i++) {
				if(x >= d[root][i][0]) ans = i;
				else if(x >= d[root][i][1]) ans = i;
				else break;
			}
			printf("%d\n", ans);
		
		}
	//	for(int i = 1; i <= n; i++) printf("%d %d\n", d[root][i][0], d[root][i][1]);
	}
	return 0;
}



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: