题意:FJ同学去买东西,东西的价值为T,他和卖家都有N种金币,FJ希望交易完成时金币变化最小。
求最少的金币变化数量。FJ的金币个数有限,卖家的金币数目无限。
思路:背包问题,FJ的每种金币个数有限可以看做是多重背包问题,卖家的金币数目无限可以看做是完全背包问题。
设DP1[i]为FJ付款为i时的最小金币数,设DP2[i]为卖家找钱为i时的最小金币数。
则DP1[i+T]+DP2[i]就是所求的最小金币变化数量(DP1用多重背包求解,DP2用完全背包求解)
PS:这里的背包求得是最小价值,且要恰好装满。故初始化数组时应 DP[0]=0,DP[1~MAXN]=INT_MAX;
/*******************
* Author;fisty
* Data:2014-11-18
* poj3260
*完全背包+分组背包
*******************/
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAX_N 224000
#define INF 1000000000
int dp1[MAX_N],dp2[MAX_N];
int v[120], c[10000];
int n,m;
int m_sum = 0;//FJ可以付的最多钱数
int solve(){
for(int i = 0;i <= m_sum; i++){
dp2[i] = INF;
dp1[i] = INF;
}
dp1[0] = 0;
dp2[0] = 0;
//对payment实行分组背包
for(int i = 1;i <= n; i++){
if(v[i]*c[i] >= m_sum){
for(int j = v[i]; j <= m_sum; j++){
dp1[j] = min(dp1[j], dp1[j-v[i]] + 1);
}
}else{
int k = 1;
while(k < c[i]){
for(int j = m_sum; j >= k*v[i]; j--){
dp1[j] = min(dp1[j], dp1[j-k*v[i]] + k);
}
c[i] -= k;
k *= 2;
}
for(int j = m_sum; j >= c[i]*v[i]; j--){
dp1[j] = min(dp1[j], dp1[j-v[i]*c[i]] + c[i]);
}
}
}
//对charge进行完全背包
for(int i = 1;i <= n; i++){
for(int j = v[i]; j <= m_sum; j++){
dp2[j] = min(dp2[j-v[i]]+1, dp2[j]);
}
}
int ans = INF;
for(int i = 0;i <= m_sum - m; i++){
ans = min(ans, dp1[i+m] + dp2[i]);
}
return ans == INF ? -1 : ans;
}
int main(){
scanf("%d%d", &n, &m);
for(int i = 1;i <= n; i++){
scanf("%d", &v[i]);
m_sum = max(m_sum, v[i]);
}
for(int i = 1;i <= n; i++){
scanf("%d", &c[i]);
}
m_sum = m_sum * m_sum + m + 1;
int ans = solve();
printf("%d\n", ans);
return 0;
}