CF632E Thief in a Shop

题意

一个背包,nn 种物品,第 ii 种物品价值为 aia_i,每种物品无穷个,只能拿 kk 个物品,问有哪些可能的价值?

分析

乍一看是一个完全背包模板,于是我们设 dpjdp_j 为当价值为 jj 时最少需要的物品数,可以写出方程式 dpj=min(dpj,dpjai+1)dp_j=\min(dp_j,dp_{j-a_i}+1),仅当 dpj=kdp_j=k 时,输出 ii

#include<bits/stdc++.h>
using namespace std;
const int N=1010,M=1e6,inf=1010;
int n,k,a[N],dp[N*N];
int main()
{
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    	cin>>a[i];
    for(int i=1;i<=M;i++)
    	dp[i]=inf;
    for(int i=1;i<=n;i++)
    	for(int j=a[i];j<=M;j++)
    		dp[j]=min(dp[j],dp[j-a[i]]+1);
    for(int i=0;i<=M;i++)
    	if(dp[i]==k)
    		cout<<i<<" ";
	return 0;
}

然而连样例都过不了。

我们参考一下样例来找找原因:

3 2
1 2 3

原来在我们的计算里 dp3=1dp_3=1,但其实 dp3=2=kdp_3=2=k 也是可行的。

那么有没有什么办法可以把 11 个物品凑成 22 个物品呢?

我们假设有一个最好的情况,我们已选物品的价值加上其他物品的价值后总价值不变,但总数量却能涨到 kk 个,在此样例中,设另外那个物品价值为 xx,那么就相当于 3+x=3,1+1=2=k3+x=3,1+1=2=k,即 x=0x=0。也就是说,我们只要有价值为 00 的物品,就可以凑成,可题目中 1ai10001≤a_i≤1000 呀!

我们考虑之前的那个状态转移方程的变形:

若保证任意 aiva_i≥v,则之前的转移方程为 dpj=min(dpj,dpj(aiv)+1)dp_j=\min(dp_j,dp_{j-(a_i-v)}+1),设此时的某个答案为 ansans,则真正答案可以转化为 ans+k×vans+k\times v,也就是把之前缺少的价值补回来。

了解了这个变形,我们只需取 v=mini=1naiv=\min_{i=1}^na_i,就可以得到上面价值为 00 的物品。

于是我们就用新的状态转移方程求出 dpdp 数组,只要 dpikdp_i≤k,即需要的最少物品数不大于 kk 个(缺少的可以用 00 补),就算作一个答案。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1010,M=1e6,inf=1010;
int n,k,a[N],dp[N*N],v=inf;
int main()
{
    cin>>n>>k;
    for(int i=1;i<=n;i++)
	{
    	cin>>a[i];
		v=min(v,a[i]);
	}
    for(int i=1;i<=M;i++)//预处理赋极大值
    	dp[i]=inf; 
    for(int i=1;i<=n;i++)//减去最小值 
    	a[i]-=v; 
    for(int i=1;i<=n;i++)//普通完全背包 
    	for(int j=a[i];j<=M;j++)
    		dp[j]=min(dp[j],dp[j-a[i]]+1);
    for(int i=0;i<=M;i++)
    	if(dp[i]<=k)
    		cout<<i+k*v<<" ";
	return 0;
}
posted @ 2021-08-04 17:57  luckydrawbox  阅读(13)  评论(0)    收藏  举报  来源