zoukankan      html  css  js  c++  java
  • P3985 不开心的金明

    金明今天很不开心,家里购置的二手房就要领钥匙了,房里并没有一间他自己专用的很宽敞的房间。更让他不高兴的是,妈妈昨天对他说:“你需要购买哪些物品,怎么布置,你说了不算(有很大的限制),而且不超过W元钱。”。今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的W元。于是,他把每件物品规定了一个重要度整数p_ipi表示。他还从因特网上查到了每件物品的价格v_ivi(都是整数元)。

    妈妈看到购物单后进行了审查,要求购物单上所有的物品价格的极差(最贵的减去最便宜的)不超过3(当然金明至今不知道为什么会这样)。他希望在不超过W元(可以等于W元)的前提下,使购买的重要度总和sum p_ipi的最大。

    请你帮助金明设计一个满足要求的购物单,你只需要告诉我们重要度的最大的和。

    输入格式

    输入的第1行,为两个正整数,用一个空格隔开:

    n W (其中W表示总钱数,n为希望购买物品的个数。)

    从第2行到第n+1行,第j行给出了编号为j-1的物品的基本数据,每行有2个非负整数v p (其中v表示该物品的价格,p表示该物品的重要度)

    输出格式

    输出只有一个正整数,为不超过总钱数的物品的重要度的总和的最大值

    输入输出样例

    输入 #1
    5 10
    2 800
    5 400
    5 300
    3 400
    2 200
    
    输出 #1
    1600

    说明/提示

    1 le N le 1001N100

    1 le W le 10^91W109

    1 le vi le 10^91vi109

    对所有的 i=1,2,3,…,Ni=1,2,3,,N,min(v_i) le v_i le min(v_i)+3min(vi)vimin(vi)+3.

    1 le p_i le 10^71pi107

    解析:

    这题明显是01背包问题

    但是 n<=100,w<=10^9,

    01背包的时间复杂度和空间复杂度都为O(n*w),即超时也超空间。

    01背包能得部分分

    代码如下:

    //纯01背包能过6个点,其他超空间和时间
    //题目有一个特点最大的价格和最小价格的差值<=3 
    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    using namespace std;
    const int maxn=100+10;
    const int maxm=10000000;
    int n,w;
    int v[maxn],p[maxn], f[maxm];
    int main(){
    	scanf("%d%d",&n,&w);
    	for(int i=1;i<=n;i++){
    		scanf("%d%d",&v[i],&p[i]);
    	}
    	for(int i=1;i<=n;i++){
    		for(int j=w;j>=v[i];j--){
    			if(f[j]<f[j-v[i]]+p[i]){
    				f[j]=f[j-v[i]]+p[i];
    			}	
    			
    		//	cout<<f[j]<<" ";
    		}
    	//	cout<<endl;
    	}
    	printf("%d
    ",f[w]);	
    	return 0;
    }
    

     

    这里物品数量n<=100,能不能用搜索做呢?

    搜索的时间复杂度是O(2^n),可以看出来也超时。

    加上剪枝后,搜索能过8改点

    代码如下:

    //纯01背包能过6个点,其他超空间和时间
    //题目有一个特点最大的价格和最小价格的差值<=3 
    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    using namespace std;
    const int maxn=100+10;
    int minn=1200000000;
    int n,w,ans=0;
    int v[maxn],p[maxn],sum[maxn];
    void dfs(int i,int s,int c){
    	if(i==n+1){
    		ans=max(ans,s);
    		return ;
    	}
    	if(c<minn) {//剪枝1,没看到效果 
    		ans=max(ans,s);
    		return ;
    	}
    	if(s+sum[n]-sum[i-1]<ans) return ;//剪枝2效果比较明显,多过了3个数据点 
    	dfs(i+1,s,c);
    	if(c>=v[i])dfs(i+1,s+p[i],c-v[i]);
    	//
    }
    int main(){
    	scanf("%d%d",&n,&w);
    	for(int i=1;i<=n;i++){
    		scanf("%d%d",&v[i],&p[i]);
    		minn=min(v[i],minn);
    		sum[i]=sum[i-1]+p[i];
    	}
    	//cout<<minn<<endl;
    	dfs(1,0,w);
    	printf("%d
    ",ans);	
    	return 0;
    }  

     充分利用题目给定的条件(动规+贪心):(1)题目有一个特点最大的价格和最小价格的差值<=3

    (2)n件物品的价值之和肯定是>=w 

    //纯01背包能过6个点,其他超空间和时间
    //题目有一个特点最大的价格和最小价格的差值<=3 
    /*假设买100件最便宜的花费x元,则买100件最贵的花费x+3*100 
    当最便宜的minn>300时,100件物品的差值最大为300,这300元一件物品也买不到
    即此时,买最贵的和买最便宜的,能买的件数是一样的,不受价格影响。 
    这样我们就可以使用贪心策略,按重要程度从到底来进行购买了。
    题目中隐含的第二个条件:想买的东西太多了,肯定会超过妈妈限定的W元
    也就是说n件物品的价值之和肯定是>=w 
    */ 
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int maxn=100+10;
    const int maxm=340000;
    int n,w,minn=2e9;
    int maxx=-1;
    int v[maxn],p[maxn], f[maxm];
    int cmp(int x,int y){
    	return x>y;
    }
    int main(){
    	scanf("%d%d",&n,&w);
    	for(int i=1;i<=n;i++){
    		scanf("%d%d",&v[i],&p[i]);
    		minn=min(minn,v[i]);//求价格的最小值 
    		maxx=max(maxx,v[i]);
    	}
    	if(minn<=300){//此时价格之和最大值303*100=30300; 
    		//int t=min(w,30400);//输出结果和j的取值,注意要用最大值min(w,30300+100 )
    		for(int i=1;i<=n;i++){
    			for(int j=w;j>=v[i];j--){//j的值不能用w(w<10^9),用能取到的最大值 30300+100
    					f[j]=max(f[j],f[j-v[i]]+p[i]);	
    			}
    		}
    		printf("%d
    ",f[w]);//输出结果注意要用最大值min(w,30300+100 ),因为题目中说物品价值之和大于W,此处可以直接使用w 
    	}
    	else {//此时价值之和很大,可是却不用数组了 
    		int sum=0;
    		sort(p+1,p+n+1,cmp);//从大到小排序
    		for(int i=1;i<=w/maxx;i++) sum+=p[i]; 
    		printf("%d
    ",sum);
    	} 
    	return 0;
    }
    

      

  • 相关阅读:
    各种知识点
    链表
    滑动窗口
    数组

    【转】无重复字符的最长子串
    【转】荷兰国旗问题 三指针排序
    【转】回溯思想团灭排列、组合、子集问题
    【LeetCode】45. 跳跃游戏 II
    动态分配内存初始化二维数组
  • 原文地址:https://www.cnblogs.com/ssfzmfy/p/12807143.html
Copyright © 2011-2022 走看看