CSP 2019 第三题:纪念品

CSP 2019 第三题:纪念品
题目链接
题目:

在这里插入图片描述
题意:
数据给出能预测的天数,纪念品种类,持有金币。每天对金币进行买卖,求买卖后的金币最大值(如何赚得更多)
知识点考:
动态规划
思路:

  • 按照题意,能够知道赚得最多就是保证每天都赚最多就行。最简单想法就是第二天根据某纪念品的涨幅,先买涨价最多的,如果持有金币还有,就继续买涨价次之的币种(可以结构体排序,然后根据剩余一次购买)。这个方法真的很贪心,每次都是买“性价比”最高的,但是这个方法只能局部最优,举个例子:我有100元,某个纪念品初值25元,能赚6元,另一个纪念品初值20元,能赚5元。按照贪心算法,定是要买赚8元的,只赚64=24元,但是如果我买初值20元的,就是5*5=25。所以这个时候看出来,明显不是最优的。所以贪心算法求最值问题时,不一定能办到全部最优,个别样例倒是能过。
  • 涉及最值问题,存在重叠子问题时,优先想到用动态规划来做。
状态转移方程:
- 明确状态:每天根据买卖,金币会变化,根据金币大小选择买不买当天纪念品
- 明确选择:每天选择买卖时,通常是判断买某个纪念品后的利润+买之前持有金币的利润是否变大(每次买一个纪念品,这样遍历时就能判断是买纪念品A还是买纪念品B划算,而不是以为的买涨幅最大的纪念品)
- 明确数组或者dp函数定义:所以置数组dp[i]一维数组放每天金币为i时能求的利润最值就行

数据约束:
数据相对比较小,注意数组范围100就行
代码注意:

  • 如果是边输入数据边处理是否购买,那么i一定是从1开始,并且要处理第0排的数据,否则一开始dp数据就会不准确
  • 如果输入完数据,遍历时,i就从第二排开始就行
  • 最后每行处理完要初始化我们的dp数组

参考代码:

#include<bits/stdc++.h>
#define MAX_N 105
using namespace std;
int a[MAX_N][MAX_N];
//动态规划思想处理
int dp[10005] ={0};//结果只和金币m有关,dp[i]表示持有i个金币时利润最大值 
int main(){
	int t,n,m;
	cin>>t>>n>>m;	
	memset(a,1e4 ,sizeof(a));
	for(int i=1;i<=t;i++){
		for(int j=1;j<=n;j++){
			cin>>a[i][j]; 
				int k0 = a[i][j]-a[i-1][j]; //差值>0才做记录 
				if(k0 > 0){ 
					for(int p=a[i-1][j];p<=m;p++){
						//记录最低金币到金币m区间买该金币后的收益
						dp[p] = max(dp[p] , dp[p-a[i-1][j]] + k0);//买之前的利润(当前金币下的最有买法)和买之后的利润做对比 
					} 
				}
		}
				m += dp[m];
//		初始化结构体
		memset(dp,0,sizeof(dp)); //每一行遍历完后就重置数组 
	} 
	cout<<m; 
	return 0;
}

贪心算法试了一下,果然是样例很多不会过,仅35%,不能AC;
贪心算法如下:

#include<bits/stdc++.h>
#define MAX_N 105
using namespace std;
int a[MAX_N][MAX_N];
//定义一个结构体数组,储存金币差值,和上一天的金币价格 ,金币遍历,从差值最高的开始 
struct stu{
	int k;//记录差值
	int m0;
}s[MAX_N];
int cmp(stu st1,stu st2){
	return  st1.k>st2.k;
} 
int main(){
	int t,n,m,mm=1e4;//mm存储一行的最小值 
	cin>>t>>n>>m;
//	处理第0排数据
	memset(a,1e4 ,sizeof(a));
	for(int i=1;i<=t;i++){
		for(int j=1;j<=n;j++){
			cin>>a[i][j]; 
			if(j-1>=0){
				int k0 = a[i][j]-a[i-1][j]; //差值>0才做记录 
				if(k0 > 0){
					s[j].k = k0;//差值
					s[j].m0= a[i-1][j];//上一个数据的值 
					if(a[i-1][j]<mm) mm=a[i-1][j];
				}
			}
		}
		sort(s+1,s+n,cmp);
		int num =0,earn =0;//赚到的最多的金币+可能还能买到的 
		int beleft = m,p=1;
		while(beleft>=mm&&p<=n){
			if(beleft>=s[p].m0&&s[p].k>0){
				num = beleft/s[p].m0;
				earn +=  num*s[p].k;
				beleft -= num*s[p].m0;
			}
			p++;
		}
		m += earn;
		mm=1e4;//初始化; 
//		初始化结构体
		memset(s,0,sizeof(s));
	} 
	cout<<m; 
	return 0;
}
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值