[CSP-J 2019]纪念品 题解
洛谷链接
考场上想了两个钟没想出来,自闭了。
相信是一位掌握DP的同学都看得出来这道题一定是DP,但估计没几个人可以一眼看出来。
我绝望地看了眼T4,然后完全绝望……
进入正题
我们先从一个个点来说。
10%
这应该是最简单的了,直接输出即可……
15%(1)
这里,我们只需用到贪心来做。
当一段连续上升的数出现后,直接在最高点套现,然后在第二天买入,即可得到最优解。
附上代码段
int i=1;
while(a[i][1]>a[i+1][1])i++;
e=d/a[i][1];
d-=e*a[i][1];
while(i<b)
{
if(a[i][1]>a[i+1][1]){d+=e*a[i][1];e=d/a[i+1][1];d-=e*a[i+1][1];}
i++;
}
d+=e*a[b][1];
cout<<d;
15%(2)
在这里,我们可以将第一天的买入价格当做价格,第二天的价格当做价值。就变成了一个完全背包。
for(int i=1;i<=b;i++)
for(register int j=a[1][i];j<=10000;j++)
dp[i][j]=max(dp[i][j],dp[i][j-a[1][i]]+a[2][i]);
for(int i=1;i<=10001;i++) f=max(f,dp[b][i]);
cout<<f;
除10%外90%
做完了15%(2),我们就可以大胆做出假设:
我们可以将每一天当做价格,第二天当做价值!
事实上,这个假设完全正确!
为什么呢?
支持这条理论的一句话起了重要作用:
每天卖出纪念品换回的金币可以立即用于购买纪念品,当日购买的纪念品也可以当日卖出换回金币。
这样我们可以用数组
a
[
i
,
j
,
k
]
a[i,j,k]
a[i,j,k]来进行计算,其中
i
i
i是最大价格,
j
j
j是选择前
j
j
j个数,
k
k
k是价值。
但是这样空间太大了,该如何优化呢?
其实,存储最大价格是没必要的,前一天获得的最大价值就是后一天的背包容量。
然后就剩下了
a
[
j
,
k
]
a[j,k]
a[j,k]
学过背包的肯定知道完全背包的空间如何优化到
O
(
n
)
O(n)
O(n),我就不多阐述。
代码
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
int a[1001][1001],b,c,d,e,f,dp[100001];
int main()
{
//freopen("souvenir.in","r",stdin);
//freopen("souvenir.out","w",stdout);
cin>>b>>c>>d;
e=d;
if(b==1){cout<<d;return 0;}
for(int i=1;i<=b;i++)
{
if(i!=2)e+=dp[e];
memset(dp,0,sizeof(dp));
for(register int j=1;j<=c;j++)scanf("%d",&a[i][j]);
for(register int j=1;j<=c;j++)
for(register int k=a[i-1][j];k<=e;k++)
if(a[i][j]-a[i-1][j])dp[k]=max(dp[k],dp[k-a[i-1][j]]+(a[i][j]-a[i-1][j]));
}
cout<<e+dp[e];
}
这里至于为什么要
i
f
(
i
!
=
2
)
if(i!=2)
if(i!=2),是因为第二天其实是不用加的。
可怜我想到了完全背包,却没有想出正解

浙公网安备 33010602011771号