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;
}