[oiclass1456]偷天换日:树上背包

题目

神偷对艺术馆内的名画垂涎欲滴准备大捞一把。
艺术馆由若干个展览厅和若干条走廊组成。每一条走廊的尽头不是通向一个展览厅,就是分为两个走廊。每个展览厅内都有若干幅画,每副画都有一个价值。经过走廊和偷画都是要耗费时间的。
警察会在 \(n\) 秒后到达进口,在不被逮捕的情况下你最多能得到的价值。

输入格式

第一行一个整数 \(n(n≤600)\)
第二行若干组整数,对于每组整数 \((t,x)\)\(t\) 表示进入这个展览厅或经过走廊要耗费 \(t\) 秒的时间,若 \(x>0\) 表示走廊通向的展览厅内有 \(x\) 幅画,接下来 \(x\) 对整数 \((w,c)\) 表示偷一幅价值为 \(w\) 的画需要 \(c\) 秒的时间。若 \(x=0\) 表示走廊一分为二。 \((t,c≤5; x≤30)\)
输入是按深度优先给出的。房间和走廊数不超过 \(300\) 个。

输出格式

仅一个整数,表示能获得的最大价值。

输入样例

50
5 0 10 1 10 1 5 0 10 2 500 1 1000 2 18 1 1000000 4

输出样例

1500

样例解释

image

题解

树上背包,将每个通道看做一个点,如果通道连接展览厅,那么这个通道就是叶子结点。对于叶子结点,跑一遍01背包求出各个时间的最优价值。对于非叶子结点,跑一遍分组背包。注意处理边界问题,细节见代码。

代码

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,f[305][605],tot,w[305],c[305];
void dfs(int u){
	int t,x;
	scanf("%d %d",&t,&x);
	t*=2;//通道时间,一进一出,时间翻倍 
	if(x>0){
		for(int i=1;i<=x;i++){
			scanf("%d %d",&w[i],&c[i]);
		}
		for(int i=1;i<=x;i++){ //01背包,求出各种状态 
			for(int j=n;j>=t+c[i];j--){
				f[u][j]=max(f[u][j],f[u][j-c[i]]+w[i]);
			}
		}
	}else{
		int v1=++tot;
		dfs(v1);
		int v2=++tot;
		dfs(v2);
		for(int i=n;i>=t;i--){ //类似分组背包,但这里只有两个分支 
			for(int j=0;j<=i-t;j++){ //枚举左儿子花了j的时间,特别注意边界,需要留出t的时间来进出 
				f[u][i]=max(f[u][i],f[v1][j]+f[v2][i-j-t]);//右儿子花了i-t-j的时间 
			}
			
		}
	}
}
int main(){
	scanf("%d",&n);
	n--;//必须在n秒前跑出来
	dfs(++tot); 
	printf("%d",f[1][n]);
}
posted @ 2022-01-11 19:57  chxulong  阅读(88)  评论(0编辑  收藏  举报