【GDOI2017模拟8.11】选择

之前漏了一天的blog,补上。

Description

有n中物品,每种物品有无限个,第i种物品的单价为ai。
问从中选出k个物品的价格有多少种,以及这些钱数。
n,k,ai<=500

Solution

首先,可以有一个很显然的DP模型。
设F[i][j][k]表示前i个物品,选了j个,能不能达成和为k。
转移显然,但是状态有N^4个,会炸。
怎么办呢?我们肯定要优化掉一维。i和k显然无法优化,我们思考一下如何优化j。
我们先把所有的ai减去min(ai)(一下简称min)。
我们再设F[i][k]表示前i个物品,达成和为k最少需要买几个物品。
如果买了x个,和为sum,那么真实的和就是sum+k*min。
因为这x个的实际价格都要多min,加上x*min,剩下的k-x个相当于买min,加上(k-x)*min.
然后就可以O(N^3)来做了。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 505
using namespace std;
int n,k,f[N*N],a[N];
int main() {
    scanf("%d%d",&n,&k);int mi=0x7fffffff,mx=0;
    fo(i,1,n) scanf("%d",&a[i]),mi=min(mi,a[i]),mx=max(mx,a[i]);
    memset(f,127,sizeof(f));f[0]=0;
    fo(i,1,n) {
        a[i]-=mi;if (!a[i]) continue;
        fo(j,a[i],k*(mx-mi)) f[j]=min(f[j],f[j-a[i]]+1);
    }
    fo(i,0,k*(mx-mi)) if (f[i]<=k) printf("%d ",i+mi*k);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值