题意
拿着 n 种硬币,每种硬币面值 vi,数量 ci,去商店购买价值 t 的物品。商店也同样有这些种类的硬币,但数目无限。问付钱和找零的过程中,至少 需要多少个硬币。
思路
付钱是多重背包,找零是完全背包。
有意思的地方是付款金额的上界问题,网上证明了 maxv * maxv + t。然而证明过程一知半解。其实猜一猜也就过了。
链接
http://poj.org/problem?id=3260
代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 100 + 10;
const int maxv = 1e5 + 10;
const int inf = 0x3f3f3f3f;
int n, T, sum;
int V[maxn], C[maxn];
int dp[maxv], dp1[maxv];
int deq[maxv], deqv[maxv];
void solve(int *dp){
dp[0] = 0;
for(int i = 0; i < n; i++){
for(int a = 0; a < V[i]; a++){
int s = 0, t = 0;
for(int j = 0; j * V[i] + a <= sum; j++){
int val = dp[j * V[i] + a] - j;
while(s < t && deqv[t - 1] >= val) t--;
deq[t] = j;
deqv[t++] = val;
dp[j * V[i] + a] = deqv[s] + j;
if(deq[s] == j - C[i]){
s++;
}
}
}
}
}
int main(){
scanf("%d %d", &n, &T);
for(int i = 0; i < n; i++){
scanf("%d", V + i);
}
for(int i = 0; i < n; i++){
scanf("%d", C +i);
}
sum = 1e5;
memset(dp, inf, sizeof dp);
solve(dp);
memset(C, inf, sizeof C);
memset(dp1, inf, sizeof dp1);
solve(dp1);
int res = inf;
for(int i = T; i <= sum; i++){
res = min(res, dp1[i - T] + dp[i]);
}
if(res >= 10000) printf("-1\n");
else printf("%d\n", res);
return 0;
}