ACM刷題之路(二十四)HDU 2844 多重揹包轉換 Coins

題目鏈接:傳送門

Coins

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 24496    Accepted Submission(s): 8740

Problem Description

Whuacmers use coins.They have coins of value A1,A2,A3...An Silverland dollar. One day Hibix opened purse and found there were some coins. He decided to buy a very nice watch in a nearby shop. He wanted to pay the exact price(without change) and he known the price would not more than m.But he didn't know the exact price of the watch.

You are to write a program which reads n,m,A1,A2,A3...An and C1,C2,C3...Cn corresponding to the number of Tony's coins of value A1,A2,A3...An then calculate how many prices(form 1 to m) Tony can pay use these coins.

 

題意:

3 10  // N=3 三個硬幣 M = 10 所求最大方案價值爲10

1 2 4 //三個硬幣的價值分別爲1 、 2 、4

2 1 1//三個硬幣的數量分別爲2 、1 、 1

求 能否滿足用這三種硬幣 湊出價值1 到 10 的方案 

比如1 = 1;2 = 1 + 1;3 = 2 + 1;4 = 4;5 = 4 + 1;6 = 4 + 2;7 = 4 + 2 + 1;8 = 4 + 2 + 1 + 1;

所以1到10可以滿足8個幣值的方案,輸出8.

分析:

其實這題也看似母函數的模板題,但這周再集訓學dp動態規劃,所以我用多重揹包來AC。

我首先想到的思路是多重揹包,轉01揹包,加一個二進制優化,就是下列代碼所示:

        for (int i = 0; i < n; i++) {
            int bei = 1;
            while (a[i].geshu > 0) {
                if (a[i].geshu & 1) {
                    b[len++] = a[i].jiazhi * bei;
                }
                bei *= 2;
                a[i].geshu /= 2;
            }
        }

後來就一直超時,加了輸入掛還是超時,所以試了一種多重揹包轉換爲二進制01揹包+完全揹包的方法。

即:如果你某個硬幣的數量乘以幣值,大於等於要求的M(第一行第二個數),那麼就可以看作是完全揹包來做題了,套完全揹包的模板:(有點斷章取義,可以從最後的總代碼來看)

if (jia[i] * shu[i] >= m)
{
	for (int j = jia[i]; j <= m; j++) 
        {
    	    dp[j] = maxx(dp[j], dp[j - jia[i]] + jia[i]);
	}
}

如果某個硬幣的數量乘以幣值,不到要求的M,那麼就需要轉化爲2進制01揹包了。

代碼如下所示:

else 
{
    for (int k = 1; k  <= shu[i]; k *= 2)
    {
	for (int j = m; j >= jia[i] * k; j--) 
        {
		dp[j] = maxx(dp[j], dp[j - jia[i] * k] + jia[i] * k);
	}
	shu[i] -= k;
    }
	if (shu[i] > 0)
        {
		for (int j = m; j >= shu[i] * jia[i]; j--) 
                {
			dp[j] = maxx(dp[j], dp[j - shu[i] * jia[i]] + shu[i] * jia[i]);
	        }
	}
}

剛開始初始化dp數組爲負無窮,且dp[0] = 1;

如果dp[i]可以爲正數,那麼肯定是從dp[0]加硬幣加上來的,所以就可以滿足剛好湊到i價值的情況。

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int maxx(int x, int y) {
	return x > y ? x : y;
}
int jia[102];
int shu[102];
int dp[100010];
int main()
{
	int n, m;
	while (~scanf_s("%d%d", &n, &m)) {
		if (n == 0 && m == 0) break;
		for (int i = 0; i < n; i++) {
			scanf_s("%d", &jia[i]);
		}
		for (int i = 0; i < n; i++) {
			scanf_s("%d", &shu[i]);
		}
		fill(dp, dp + 100010, -999999);
		dp[0] = 1;
		for (int i = 0; i < n; i++) {
			if (jia[i] * shu[i] >= m) {
				for (int j = jia[i]; j <= m; j++) {
					dp[j] = maxx(dp[j], dp[j - jia[i]] + jia[i]);
				}
			}
			else {
				for (int k = 1; k  <= shu[i]; k *= 2) {
					for (int j = m; j >= jia[i] * k; j--) {
						dp[j] = maxx(dp[j], dp[j - jia[i] * k] + jia[i] * k);
					}
					shu[i] -= k;
				}
				if (shu[i] > 0) {
					for (int j = m; j >= shu[i] * jia[i]; j--) {
						dp[j] = maxx(dp[j], dp[j - shu[i] * jia[i]] + shu[i] * jia[i]);
					}
				}
			}
		}
		int ans = 0;
		for (int i = 1; i <= m; i++) {
			if (dp[i]) {
				ans++;
			}
		}
		printf("%d\n", ans);
	}
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章