【BZOJ 1716】 [Usaco2006 Dec]The Fewest Coins 找零錢(揹包dp)

題目

Description

農夫John想到鎮上買些補給。爲了高效地完成任務,他想使硬幣的轉手次數最少。即使他交付的硬幣數與找零得到的的硬幣數最少。 John想要買T(1<=T<=10000)樣東西。有N(1<=n<=100)種貨幣參與流通,面值分別爲V1,V2…Vn (1<=Vi<=120)。John有Ci個面值爲Vi的硬幣(0<=Ci<=10000)。我們假設店主有無限多的硬幣,並總按最優方案找零。

Input
  • Line 1: 兩個整數 N 與 T 。

  • Line 2: N 個數,表示 V1, V2, …Vn。

  • Line 3: N 個數,表示 C1, C2, …Cn。

Output
  • Line 1: 一個整數,表示最優方案的轉手次數,如無解輸出-1。
Sample Input

3 70
5 25 50
5 2 1

Sample Output

3

原題傳送門

思路

題目思維難度並不大。
一個多重揹包+完全揹包。
我們對約翰做多重揹包,對老闆做完全揹包。
然而,錯了n次,極度懷疑我自己。
調了一個小時,發現,最大值設的太大了。。。 溢出了。。。
哎……

代碼

#include <bits/stdc++.h>

using namespace std;
const int M = 110, N = 10000+120*120+10;
int dp1[N], dp2[N], v[M], c[M];

int main() {
	ios::sync_with_stdio(false);
	int n, t, maxn = 0; cin >> n >> t;
	for (int i = 0; i < n; i++)
		cin >> v[i], maxn = max(maxn, v[i]);
	maxn = maxn*maxn;
	int maxm = t+maxn;
	for (int i = 0; i < n; i++) cin >> c[i];
	for (int i = 0; i <= maxm; i++) dp1[i] = dp2[i] = 129237174;
	dp1[0] = 0, dp2[0] = 0;
	for (int i = 0; i < n; i++) {
		int k = 1, sum = 0;
		while (sum < c[i]) {
			for (int j = maxm; j >= v[i]*k; j--)
				dp1[j] = min(dp1[j], dp1[j-v[i]*k]+k);
			sum += k;
			if (sum+k*2 > c[i]) k = c[i]-sum;
			else k *= 2;
		}
	}
	for (int i = 0; i < n; i++)
		for (int j = v[i]; j < maxn; j++)
			dp2[j] = min(dp2[j], dp2[j-v[i]]+1);
	int ans = 129237154;
	for (int i = t; i <= maxm; i++)
		ans = min(ans, dp1[i]+dp2[i-t]);
	if (ans == 129237154) cout << -1 << endl;
	else cout << ans << endl;
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章