UVA 674 硬币问题(完全背包、枚举)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Tianweidadada/article/details/82990233

题意:给定金额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;
	}

猜你喜欢

转载自blog.csdn.net/Tianweidadada/article/details/82990233
今日推荐