zoukankan      html  css  js  c++  java
  • cf 816E Karen and Supermarket

    题目大意

    给定(n)一颗树,每个点上有一个物品
    每个物品有价格(c[i])
    有优惠券,能使价格减少(d[i])
    但是使用优惠券的前提时购买该物品,且父亲也使用优惠券
    给定钱包余额(lim)
    求最多能买多少物品
    (nle 5000, c[i],d[i],limle 10^9)

    分析

    树上背包
    由于价值的数字很大,不能用钱来表示状态,个数表示dp值
    只能先计算购买(k)个的最少价钱,再判断限制

    (f[x][i][0])表示(x)这个点不用优惠券,子树中买了(i)个物品的最低价钱
    (f[x][i][1])表示(x)这个点不用优惠券,子树中买了(i)个物品的最低价钱

    使用子树不断合并到当前点的方法,可以使复杂度变为(n^2)
    (每个点对在贡献一次(O(1))复杂度后合并到一个状态中,相互不会再产生贡献)

    做法

    (x)为当前点,(y)为该点的儿子

    边界条件
    f[x][0][0]=0 ,f[x][0][1]=INF
    f[x][1][0]=c[i], f[x][1][1]=c[i]-d[i]

    合并转移(k=i+j)
    f[x][k][0]=f[x][i][0]+f[y][j][0]
    f[x][k][1]=f[x][i][1]+min(f[y][j][0],f[y][j][1])

    实现时会算重(因为是01背包)
    法1:枚举和(k),逆着扫(k),再枚举i或j中的一个
    法2:枚举(i),逆着扫(i),再枚举(j)

    solution

    #include <cstdio>
    #include <cstring>
    #include <cctype>
    #include <cmath>
    #include <algorithm>
    #include <cstdlib>
    using namespace std;
    const int M=5e3+7;
    typedef long long LL;
    
    inline int rd(){
    	int x=0;bool f=1;char c=getchar();
    	for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
    	for(;isdigit(c);c=getchar()) x=x*10+c-48;
    	return f?x:-x;
    }
    
    struct vec{
    	int g[M],te;
    	struct edge{
    		int y,nxt;
    		edge(int _y=0,int _nxt=0){y=_y,nxt=_nxt;}
    	}e[M<<1];
    	vec(){memset(g,0,sizeof g);te=0;}
    	inline void push(int x,int y){e[++te]=edge(y,g[x]);g[x]=te;}
    	inline void push2(int x,int y){push(x,y);push(y,x);}
    	inline int& operator () (int x){return g[x];}
    	inline edge& operator [] (int x){return e[x];}
    }e;
    
    int n,sz[M];
    LL lim,c[M],d[M];
    LL f[M][M][2];
    
    void dfs(int x,int fa){
    	int i,j,k,p,y;
    	sz[x]=1;
    	f[x][0][0]=0;
    	f[x][1][0]=c[x];
    	f[x][1][1]=c[x]-d[x];
    
    	for(p=e(x);p;p=e[p].nxt)
    	if((y=e[p].y)!=fa){
    		dfs(y,x);
    
    		for(k=sz[x]+sz[y];k>=0;k--)
    		for(j=0;j<=sz[y];j++) if((i=k-j)<=sz[x]){
    			f[x][k][0]=min(f[x][k][0],f[x][i][0]+f[y][j][0]);
    			f[x][k][1]=min(f[x][k][1],min(f[x][i][1]+f[y][j][0],f[x][i][1]+f[y][j][1]));
    		}
    
    		sz[x]+=sz[y];
    	}
    }
    
    int main(){
    
    	int i,x;
    	n=rd(); lim=rd();
    
    	for(i=1;i<=n;i++){
    		c[i]=rd(), d[i]=rd();
    		if(i>1) e.push(rd(),i);
    	}
    
    	memset(f,0x3f,sizeof f);
    	dfs(1,0);
    
    	int ans=0;
    	for(i=0;i<=n;i++) if(min(f[1][i][0],f[1][i][1])<=lim) ans=i;
    	printf("%d
    ",ans);
    
    	return 0;
    }
    
  • 相关阅读:
    适配者模式7(10)
    规范使用线程池与底层原理详解
    Java集合多线程安全
    CAS底层原理与ABA问题
    手写数字识别-小数据集
    深度学习-卷积
    Java并发编程volatile关键字
    朴素贝叶斯-垃圾邮件分类
    K均值算法
    mysql搭建主从复制(一主一从,双主双从)
  • 原文地址:https://www.cnblogs.com/acha/p/7141650.html
Copyright © 2011-2022 走看看