zoukankan      html  css  js  c++  java
  • 【9927】庆功会

    Time Limit: 1 second
    Memory Limit: 128 MB

    【问题描述】

    为了庆贺班级在校运动会上取得全校第一名成绩,班主任决定开一场庆功会,为此拨款购买奖品犒劳运动员。期望拨款金额能购买
    最大价值的奖品,可以补充他们的精力和体力。

    【输入格式】

    第一行二个数n(n<=500),m(m<=6000),其中n代表希望购买的奖品的种数,m表示拨款金额。
    接下来n行,每行3个数,v、w、s,分别表示第I种奖品的价格、价值(价格与价值是不同的概念)和购买的数量(买0件到s件均可
    ),其中v<=100,w<=1000,s<=10。

    【输出格式】

    第一行:一个数,表示此次购买能获得的最大的价值(注意!不是价格)。

    Sample Input

    5 1000
    80 20 4
    40 50 9
    30 50 7
    40 30 6
    20 20 1
    
    
    
    
    

    Sample Output

    1040

    【题解】

    【法1】

    这种有限制物品数量的背包问题,和完全背包的二维形式类似。只要在枚举的时候加上一个限制条件即k <= num[i]即可。

    f[i][j]表示前i个物品,所用容量不超过j所能获得的最大价值。

    f[i][j] = max{f[i-1][j],f[i-1][j-k*w[i]]+ k*c[i];

    【代码1】

    #include <cstdio>
    #include <cstring>
    
    int m,n,f[509][6010],w[509],c[509],num[509]; //f数组实际上可以换成一个二维的滚动数组。。这样很方便的。 
    
    void input_data()
    {
    	scanf("%d%d",&n,&m);
    	for (int i = 1;i <= n;i++) 
    		scanf("%d%d%d",&w[i],&c[i],&num[i]);
    }	
    
    void get_ans()
    {
    	memset(f,0,sizeof(f));			 
    	for (int i = 1;i <= n;i++)
    		{
    			for (int j = m;j >=0;j--) //其实从大到小或者从小到大都没差。因为是二维的。 
    				{
    					f[i][j] = f[i-1][j];
    					int k = 1;
    					while (k <= num[i] && k*w[i] <= j) //如果能够更新解。就更新 k要小于等于num[i] 
    						{
    							if (f[i][j] < f[i-1][j-k*w[i]] + k * c[i])
    								f[i][j] = f[i-1][j-k*w[i]] + k * c[i];
    							k++;
    						}
    				}		
    		} 
    }
    
    void output_ans()
    {
    	printf("%d",f[n][m]);	
    }
    
    int main()
    {
    	//freopen("F:\rush.txt","r",stdin);
    	input_data();
    	get_ans();
    	output_ans();
    	return 0;	
    }

    【法二】

    一维形式。

    设f[j]表示容量不超过j时,所能获得的最大价值。

    则f[j] = max{f[j],f[j-k*w[i]] + k * c[i].

    其实这种更新方式和二维的无异。只是少了一维。且更新方式固定了。只能让容量那层循环从大到小循环。

    如果还是不明白可以把法1的程序和法2的程序在i那层后面输出一下f[m-10..m],会发现两个程序,i相同时,f[i][j]和f[j]是一样的。

    【代码2】

    #include <cstdio>
    #include <cstring>
    
    int m,n,f[6010],w[509],c[509],num[509]; 
    
    void input_data()
    {
    	scanf("%d%d",&n,&m);
    	for (int i = 1;i <= n;i++) 
    		scanf("%d%d%d",&w[i],&c[i],&num[i]);
    }	
    
    void get_ans()
    {
    	memset(f,0,sizeof(f));			 
    	for (int i = 1;i <= n;i++)
    		for (int j = m;j >=w[i];j--) //换成了一维的形式。这时就一定要从后往前递减了。 
    			for (int k = 1;k <= num[i];k++) //其实更新方式和二维是一样的。先从后面的更新是因为不会影响前面f[1..j-1]
    			//而更新完f[j]之后,更新f[1..j-1]又恰好与f[j]无关了。所以f[j]的更新方式是正确的,且不会影响到后续的更新。 
    				{
    					if (j-k*w[i] < 0)
    						break;
    					if (f[j] < f[j-k*w[i]] + k*c[i])
    						f[j] = f[j-k*w[i]] + k*c[i];	
    				}
    					
    }
    
    void output_ans()
    {
    	printf("%d",f[m]);	
    }
    
    int main()
    {
    	//freopen("F:\rush.txt","r",stdin);
    	input_data();
    	get_ans();
    	output_ans();
    	return 0;	
    }



  • 相关阅读:
    Android文字跑马灯控件(文本自动滚动控件)
    Android中的“再按一次返回键退出程序”实现
    Android中 在显示ImageView时图片上面和下面都出现一段空白区间的解决办法
    问题解决The connection to adb is down, and a severe error has occured.
    android关于uses-permission权限列表
    菜鸟学习Andriod-弹窗
    Andriod使用webview控件往APP里内嵌网页
    Mysql初始化root密码和允许远程访问
    转:Vmware Exsi使用简要说明
    转:怎样在VMware ESXi上 克隆虚拟机
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7632394.html
Copyright © 2011-2022 走看看