【JZOJ5788】餐馆

Description

 K妹的胡椒粉大卖,这辣味让食客们感到刺激,许多餐馆也买这位K妹的账。有N家餐馆,有N-1条道路,这N家餐馆能相互到达。K妹从1号餐馆开始。每一个单位时间,K妹可以在所在餐馆卖完尽量多的胡椒粉,或者移动到有道路直接相连的隔壁餐馆。第i家餐馆最多需要A[i]瓶胡椒粉。K妹有M个单位的时间,问她最多能卖多少胡椒粉。

Input

第一行有两个正整数N,M。
第二行描述餐馆对胡椒粉的最大需求量,有N个正整数,表示A[i]。
接下来有N-1行描述道路的情况,每行两个正整数u,v,描述这条道路连接的两个餐馆。

Output

一个整数,表示她最多能卖的胡椒粉瓶数。

Sample Input

样例1输入
3 5
9 2 5
1 2
1 3

样例2输入
4 5
1 1 1 2
1 2
2 3
3 4

样例3输入
5 10
1 3 5 2 4
5 2
3 1
2 3
4 2
 

Sample Output

样例1输出
14

样例2输出
3

样例3输出
15
 

Data Constraint

对于10%的数据,N≤20。
对于50%的数据,N≤110。
对于100%的数据1 ≤ N, M ≤ 500,1 ≤ A[i]≤ 10^6,
第5到第10个测试点都有多个子测试。

Hint

在样例1的中,辣妹到达城市2后就恰好没时间卖辣椒粉了。

题解:

就算知道了是树形DP也不可能想出转移的,这辈子都不可能的= =
看了一下题解,大致明白了思路,这里复制一下题解内容
设f[i][j][0/1]
f[i][j][0]表示以 i 为根的子树中,花费 j 单位时间,最终回到 i 的最大收益。
f[i][j][1]表示以 i 为根的子树中,花费 j 单位时间,最终不必回到 i 的最大收益。
先走之前的子树回到根,再走下一颗子树不回根:
dp[i][j][0] + dp[son][k][1] --> dp[i][j+k+1][1]
先走之前的子树回到根,再走下一颗子树也回根:
dp[i][j][0] + dp[son][k][0] --> dp[i][j+k+2][0]
先下一颗子树回到根,再走之前的子树不回根:
dp[i][j][1] + dp[son][k][0] --> dp[i][j+k+2][1]
枚举下一个子树后,倒序枚举j和k
整个过程转移是从叶节点向上推进,因此在枚举子树的下面要初始化一下
时间复杂度为O(n^2),具体详见代码= = 

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<climits>
#include<iomanip>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#define MAXA 505
#define ipt(x) scanf("%d",&x)
using namespace std;
typedef long long LL;

int n,m,Ans;
int Pepper[MAXA],f[MAXA][MAXA][2];
bool vis[MAXA];
vector<int> son[MAXA];
void DP(int x) {
	vis[x] = 1;
	for(int i=0;i<son[x].size();i++) {
		int y = son[x][i];
		if(!vis[y]) {
			DP(y);
			for(int j=m;j>=0;j--)
		        for(int k=m-j;k>=0;k--) {
		    	if(j + k + 1 <= m)
				   f[x][j+k+1][1] = max(f[x][j+k+1][1],f[x][j][0] + f[y][k][1]);
				if(j + k + 2 <= m)
				   f[x][j+k+2][0] = max(f[x][j+k+2][0],f[x][j][0] + f[y][k][0]);
				if(j + k + 2 <= m)
				   f[x][j+k+2][1] = max(f[x][j+k+2][1],f[x][j][1] + f[y][k][0]); 
			}
		}
		
	}
	for(int i=m;i>=1;i--) {
		f[x][i][0] = max(f[x][i][0],f[x][i-1][0] + Pepper[x]);
		f[x][i][1] = max(f[x][i][1],f[x][i-1][1] + Pepper[x]);
	}
}
int main() {
	freopen("dostavljac.in","r",stdin);
	freopen("dostavljac.out","w",stdout);
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++)
	    scanf("%d",&Pepper[i]);
	for(int i=1;i<n;i++) {
		int u,v;
		scanf("%d %d",&u,&v);
		son[u].push_back(v);
		son[v].push_back(u);
	}
	DP(1);
	printf("%d",max(f[1][m][1],f[1][m][0]));
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值