[CSP-J2019] 纪念品 - 洛谷
稍微一读题,每种纪念品可以买无数次,有总钱数,价格,以及天数,应该都可以想到是完全背包。
可是到这就又犯难了:纪念品的价格每天都在变,还有手里的钱和纪念品数量在变,还要买或卖,如果都传入状态中,肯定炸空间。
我们一个一个解决:
纪念品的价格尽管每天在变,但题目给出了每天每种的价格。
手里钱、纪念品数以及买卖其实用一个简单的思路就足以解决:我们可以每天早上把手里所有纪念品全部卖掉,获得一天的本金,fufu,如果卖掉了有一些不该卖的,就再买回来,这样相当于每天每件纪念品就只有买或不买,就可以转化为典型的完全背包,这其实也是一种变相的贪心策略,我们只要保证今天一天的收益最高就行,而不用管后面几天是否能赚最多钱,因为最优情况也被保存在dp数组里,在后面的dp中会被使用并传递。
所以结论是:该背包最重要的状态就是手里的钱,因为它能保存住整体的最优情况在该日的策略,故这题的dp数组可以优化到一维。
不多说,上代码。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
int p[110][110];//每天 每种 纪念品 价格
int f[100010];//每种 纪念品
int t,n,m;//t->天数,n->纪念品,m->金币数
void iii(int ans)
{
for(int j=0;j<=ans;++j) f[j]=-1e9;
}
int main()
{
scanf("%d%d%d",
for(int i=1;i<=t;++i)
for(int j=1;j<=n;++j) scanf("%d",
int ans=m;//第一天手里有m块钱
for(int i=1;i<t;++i)
{
iii(ans);//初始化为极小值
f[ans]=ans;//每天的本金,相当于什么也不买
for(int j=1;j<=n;++j)
{
for(int k=ans;k>=p[i][j];--k)//枚举每种钱数可以赚到的最多钱
{
f[k-p[i][j]]=max(f[k-p[i][j]],f[k]-p[i][j]+p[i+1][j]);//转移方程
}
}
int tem=0;
for(int j=0;j<=ans;++j) tem=max(tem,f[j]);//算明天最多的本金
ans=tem;//明天继续赚钱
}
printf("%d",ans);
return 0;
}
这种情况f数组存的是总钱数,我们还可以存利润。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
int p[110][110];//每天 每种 纪念品 价格
int f[100010];//每种 纪念品
int t,n,m;//t->天数,n->纪念品,m->金币数
int main()
{
scanf("%d%d%d",
for(int i=1;i<=t;++i)
for(int j=1;j<=n;++j) scanf("%d",
int ans=m;//第一天手里有m块钱
for(int i=1;i<t;++i)
{
memset(f,0,sizeof(f));//初始化为0
for(int j=1;j<=n;++j)
{
for(int k=p[i][j];k<=ans;++k)//枚举每种钱数
{
f[k]=max(f[k],f[k-p[i][j]]-p[i][j]+p[i+1][j]);//转移方程
}
}
ans+=f[ans];//加上盈利的钱,明天继续赚钱
}
printf("%d",ans);
return 0;
}
个人比较喜欢存利润的方法,第一种我没搞懂
(雷电将军已出,前来还愿)