[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),是因为第二天其实是不用加的。
可怜我想到了完全背包,却没有想出正解

QAQ

posted @ 2022-08-09 20:18  Velix  阅读(361)  评论(0)    收藏  举报