csp202209-2 何以包邮?(0-1背包问题求解)

 这题可以看作是一个0-1背包问题,每本书的状态只有两个:选(1)与不选(0),但是要注意题目中要求书的价格总和不小于x,而0-1背包问题是物品容量不超过V,所以这里要转换一下,先求出n本书的总金额sum,再用sum-x,得到的就是不应该买的书的最大金额。而不应该买的书的金额越大,满足条件应该买的书的金额就越小。

对于dp[i][j]的状态表示,集合为从第1~i本书里选择一些书,其总金额不超过j;属性为总金额的最大值。

可以得出状态转移方程:

 dp[i][j]=dp[i-1][j], j<a[i]
 dp[i][j]=max(dp[i-1][j],dp[i-1][j-a[i]]+a[i]), j>=a[i]

完整代码如下:

#include <iostream>
using namespace std;

int a[35];

int n,x,sum,v;    //定义在全局区,默认初始化为0

int dp[35][300010];		//存的状态是从1-i本书里选不超过金额j的最大值

int main()
{
	cin>>n>>x;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		sum+=a[i];
	}
	
	v=sum-x;	//背包空间  应该买的书的金额大于等于x元,则不应该买的书的金额要小于等于sum-x元
	
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=v;j++)
		{
			dp[i][j]=dp[i-1][j];		//不选
			if(j>=a[i])
			{
				dp[i][j]=max(dp[i][j],dp[i-1][j-a[i]]+a[i]);  
				//求max是因为不能买的书的金额越大,能买的书的金额就越小	
			} 
		}
	}
	
	int res=sum-dp[n][v];		//注意一开始算的是不能买的书的金额 
	cout<<res;
	return 0;
}

运行结果:

 这里已经能得满分了。

动态规划时间复杂度一般可以这样计算:状态数量*状态转移计算量。

对0-1背包问题,可以对它进行一维优化,降低时间复杂度,优化后的代码如下:

#include <iostream>
using namespace std;

int a[35];

int n,x,sum,v;

int dp[300010];	

int main()
{
	cin>>n>>x;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		sum+=a[i];
	}
	
	v=sum-x;	//背包空间  买的书的金额大于等于x元,则不能买的书的金额要小于等于sum-x元
	
	for(int i=1;i<=n;i++)
	{
		for(int j=v;j>=a[i];j--)		//注意一定要逆序,否则就变成完全背包问题了 
		{
			dp[j]=max(dp[j],dp[j-a[i]]+a[i]); 
			//求max是因为不能买的书的金额越大,能买的书的金额就越小	
		}
	}
	
	int res=sum-dp[v];		//注意一开始算的是不能买的书的金额 
	cout<<res;
	return 0;
}

运行结果对比:

这里浅谈一下我所理解的优化过程,在最开始的版本中有:

for(int i=1;i<=n;i++)
{
	for(int j=1;j<=v;j++)
	{
		dp[i][j]=dp[i-1][j];		
		if(j>=a[i])
		{
			dp[i][j]=max(dp[i][j],dp[i-1][j-a[i]]+a[i]);  
		} 
	}
}

要变成一维,首先先删去第一维:

for(int i=1;i<=n;i++)
{
	for(int j=1;j<=v;j++)
	{
		dp[j]=dp[j];		
		if(j>=a[i])
		{
			dp[j]=max(dp[j],dp[j-a[i]]+a[i]);  
		} 
	}
}

dp[j]=dp[j]很明显可以省略,且j只有满足j>=a[i]时if语句才执行,故再进行简化:

for(int i=1;i<=n;i++)
{
	for(int j=a[i];j<=v;j++)
	{		
		dp[j]=max(dp[j],dp[j-a[i]]+a[i]);  
	}
}

这样写其实还有一个问题,由于j-a[i],所以dp[j-a[i]]会在dp[j]之前先被算出,实际上是第i层的状态而不是第i-1层的状态,上面的代码其实等价于dp[i][j]=max(dp[i][j],dp[i][j-a[i]]+a[i]);这样可能导致一个物品多次放入背包,这就不是完全背包问题了。故j应该逆序:

for(int i=1;i<=n;i++)
{
	for(int j=v;j>=a[i];j--)    //一定要逆序
	{		
		dp[j]=max(dp[j],dp[j-a[i]]+a[i]);  
	}
}

这样同一个物品就不会重复放入背包了,得到的结果才是正确的。

如果不是很理解,可以将结果打印输出一下,还是很明显的。

结语:这是我在学完Acwing算法基础课之后再重做去年考的csp认证题目,当时大部分人和我一样都是15个for循环暴力求解得70分,但其实只要掌握一些基础算法还是很好拿满分的。

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
题目编号为202209-2的题目是《何以?》。这道题的问题描述是关于购物车中的图书数量和最低值的计算。其中提供了两种解题方法,一种是直接无脑解,另一种是满分题解。[3] 直接无脑解的方法是通过输入购物车中的图书数量n和最低值x,然后设置一个列表a来存储每本书的价格。接着进行遍历,将每本书的价格依次存入列表a,并对列表a进行从低到高的排序。在遍历过程中,判断是否小于最小的值,如果是,则将该价格添加到集合temp中。最后,从集合temp中选择一个最小的价格作为答案输出。 满分题解的方法是在直接无脑解的基础上使用了动态规划的思想。同样是输入购物车中的图书数量n和最低值x,然后设置一个列表a来存储每本书的价格。接着,设置一个动态规划数组dp来存储每个价格的最小花费。在遍历过程中,将每本书的价格依次存入列表a,并设置一个变量pre来保存目前满足的最小花费。然后使用01背包解法,将每个地方的最优解存入dp数组中。最后,从最低值x开始遍历,找到超过x的dp[i]并输出。 综上所述,题目编号为202209-2的题目《何以?》是关于购物车中图书数量和最低值的计算问题,提供了直接无脑解和满分题解两种解题方法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [CCF-CSP真题《202209-2 何以?》思路+python满分题解](https://blog.csdn.net/weixin_53919192/article/details/127173638)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值