可怜与超市

//T4  可怜与超市
//long long!!!!我看到10的九次方了,我直接龙龙  
//不对啊,b也小于10亿啊,int就够了 
//我聪明的大脑瞬间看出,树形DP 
//题干的意思是不是1就是根?应该是,但我觉得不稳 
//f[u][0/1] 以u为根的子树,u买或者不买的能购买的最多商品 最优解(之所以这么定义因为跑不了10亿乘5000) 
//0是不买 带点背包思想,选课那种,这个注意 
//f[u][0]+=(f[v][0])
//f[u][1]+=(f[v][1],f[v][0]) 最后轮一遍最优 
//就这?????
//现在就是我不知道它是否会是一个森林,或者就是以1为根
//这样吧,我赌一把,1根,没有森林 
//不对,这样还是不好做,我这个值没法算啊
//要不还是一开始的思路,去算钱,然后再比
//关键, ....5000*5000能开嘛 ,是五千万,应该可以,
//那行,改了改了,全改了
//f[i][j][0/1] 以i为根的子树,买j件的最小价值 还是选课那个背包思想,与i的儿子进行轮k 
//那么0 1 就表示用不用优惠劵 0是不用 
//考完了我总结一下,就是:加权选课用上皇宫的状态思想 嘶,你这么一总结,这可真是一道好题,高低是把树形整透了 
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll; 
const int mx=5000+10;
int n,len;
ll b; 
int head[mx];
ll c[mx],d[mx];
ll f[mx][mx][2]; 
int size[mx];//这跟选课还有些不同,这我得开一个记录它的儿子个数
//额怎么说,其实怎么着都能写,不过我觉得这种写法还清晰一些 
struct Node{
	int from;
	int to;
	int next;
}a[mx];
void Insert(int u,int v){
	a[++len].from=u;
	a[len].to=v;
	a[len].next=head[u];
	head[u]=len; 
}
void dfs(int u){
	f[u][0][0]=0;
	f[u][1][0]=c[u];
	f[u][1][1]=c[u]-d[u];
//	printf("1k f[%d][1][1]=%lld c[%d]=%lld d[%d]=%lld\n",u,f[u][1][1],u,c[u],u,d[u]);
	size[u]=1;
	for(int i=head[u];i!=0;i=a[i].next){
		int v=a[i].to;
		dfs(v);
		//这个题的k的顺序一直是无所谓的,当前处理的子树拿出来的,从几开始轮都行
		//j的顺序必须是这样,如果是j-k,就是一开始就把总数算出了,其实就相当于一个01背包 
		//j是总容积,k每次填进去,它每次都是有一些从j-k更新的,如果从0到j,就是可能这个点前面 
		//已经用过了,我再后面又用了一次,点也是每次只能用一次 ,与DP无后效性相符 
		//如果是j+k,j+k每次从j更新,还是说它从小于它的更新,就得倒序,无后效性
		
		//如果下标为负,它会根据你的数组在内存的位置 更新你数组里的其它的值,
		//如果在前面就run 后面就会w或者多跑了很多次导致t,这个注意
		
		//关于当j-k 因为我们的答案j是从左右上来的
		// 这么说,当j=总时,k没有选择,只能全部奉上,其他都是无意义的;如果你进行j-k
		// 当k不是size[v],就需要左边付出更多,笑死,它根本没有,从这里理解它也是无意义的 
		// 然后有个细节就是k要大于j-kk,但你还是要跟0取个max,因为j也会变小
		// k在右边为了防止j-k<0 也要处理 

		//这个题是透的不能再透了
		int kk=size[u];
		size[u]+=size[v];
		for(int j=size[u];j>=0;--j){
		//	j-k<=kk j-kk<=k
			for(int k=max(0,j-kk);k<=min(j,size[v]);++k){
				f[u][j][0]=min(f[u][j][0],f[u][j-k][0]+f[v][k][0]);
				f[u][j][1]=min(f[u][j][1],min(f[u][j-k][1]+f[v][k][0],f[u][j-k][1]+f[v][k][1]));
				//第二行的两个f[u][j][1]都应该是1 半小时 
//				printf("f[%d][%d][1]=%lld\n",u,j+k,f[u][j+k][1]);
			}
		}
		
		
	/*	for(int j=size[u];j>=0;--j){
			for(int k=0;k<=size[v];++k){
				f[u][j+k][0]=min(f[u][j+k][0],f[u][j][0]+f[v][k][0]);
				f[u][j+k][1]=min(f[u][j+k][1],min(f[u][j][1]+f[v][k][0],f[u][j][1]+f[v][k][1]));
			}
		}
		size[u]+=size[v];*///加的 
		
	} 
} 
void Solve(){
	memset(f,0x3f,sizeof(f));
//	printf("%lld\n",f[1][1][1]);ok,0x3f够大 
	scanf("%d%lld",&n,&b);
	for(int i=1;i<=n;++i){
		 if(i==1){
		 	ll x,y;
		 	scanf("%lld%lld",&x,&y);
		 	c[i]=x;
		 	d[i]=y;
		 }
		 else{
		 	ll x,y;
			int z;
		 	scanf("%lld%lld%d",&x,&y,&z);
		 	c[i]=x;
	//	 	printf("y=%lld\n",y);
		 	d[i]=y;
	//	 	printf("d[%d]=%lld\n",i,d[i]);
		 	Insert(z,i);//i是z的儿子 
		 }
	}
	dfs(1);
	int ans=0;//这个地方我到是没错,试数据的时候试出来的
	//int ans=0;而不是int ans ;保持自己这个好习惯 
	for(int i=n;i>=0;--i){
//		printf("%lld %lld \n",f[1][i][0],f[1][i][1]);
		if(f[1][i][0]<=b || f[1][i][1]<=b){	
			ans=i;
			break;
		}
	}
	printf("%d\n",ans);
}
int main(){
	freopen("supermarket.in","r",stdin);
	freopen("supermarket.out","w",stdout);
	Solve();
	return 0;
} 
posted @ 2022-02-21 11:21  SMTwy  阅读(85)  评论(0编辑  收藏  举报