1. 程式人生 > >UVA 674 硬幣問題(完全揹包、列舉)

UVA 674 硬幣問題(完全揹包、列舉)

 

 

題意:給定金額n,有50,25,10,5,1這五種面值的錢,問共有多少種不同的找法(假設最少存在一種找法)。

 

這題我想了很久,都沒有找到合適的狀態轉移方程,最後看了別人寫的,又體會了半天,才算明白。

分析:

這題,寫不對原因在於,很容易就重複計算了。例如 11 中的   (11111,5,111111)與(11111,111111,5)視為一種。

那麼怎麼避免重複計算呢。

首先從小的到大列舉面額(這個必須在外面迴圈)

比如 列舉 dp[0] = 1,dp[1] = 1,dp[2] = 1,d[3] = 1,dp[4] = 1

當列舉到dp[5]的 時候 第一遍 的結果是 dp[5] += dp[5-1] 此時 dp[5] = dp[4] = 1(實際上dp[5] = 2),下一個迴圈列舉到

dp[5]的時候,dp[5] += dp[5-5] = 2。這樣單獨看某個值,最後都會遍歷到每一個面值,也就是最終會滿足dp方程

 

dp[i] = \sum\limit_{i \in[0-4]} dp[j-cost[i]]

這樣 第一遍 dp[6] += dp[6-1] =>dp[6] =  dp[5]  = 1, 第二遍 dp[6] += dp[6-5] =>dp[6] = 1 + dp[1] 不會重複。 

 

code:

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<vector>
#include<map>
#include<algorithm>
#include<string>
using namespace std;
const int N = 10000;
int cost[10] = {1,5,10,25,50};
int dp[N];

int main()
	{
		// dp[i] 表示 金錢 i 可以有多少種表示方法 
		int n;
		memset(dp,0,sizeof(dp));
		dp[0] = 1;
		for(int i = 0; i < 5; ++i){  // 兩層 迴圈不可互換 否則 重複 
			for(int j = cost[i]; j < N; ++j){
				dp[j] += dp[j-cost[i]];
			}
		}
		while(scanf("%d",&n) != EOF){
			printf("%d\n",dp[n]);
		}
	
		return 0;
	}