动态规划学习笔记1-硬币找零 2021.3.1

题意描述:

假设有几种硬币,如1、3、5,并且数量无限。请找出能够组成某个数目的找零所使用最少的硬币数。

输入格式:

第一行两个整数数t,n,表示总金额和面值总数;
第二行n个整数a1,a2…an,其中ai表示第i种硬币的面值。

输出格式:

最少硬币总数。如果无解,输出-1。

样例输入:

15 3
1 11 5

样例输出:

3

思路分析:

1.暴力搜索
  将最小值设定为一个很大的数INF,然后设立一个递归函数dfs(int amount)求解即可,其中amount代表当前待找钱的数额,递归使用dfs(amount-mz[i]),其中mz[i]代表第i种硬币的面值,递归结束条件为amount ==0,<0代表当前面值无解。全部结束后如果最小值仍为INF则无解。
此解法会造成大量重复计算,因而耗费时间。
代码就不贴了。
2.记忆化搜索
  可以用记忆化搜索节省时间。
  设立记忆数组vis,开始全部初始化为-1,递归函数改为int型,amount ==0时就不用找了,返回0,小于0时返回-1,此时代表当前面值无解,minn仍为INF,记忆数组中对应项仍为-1。否则就要改变。
代码:

#include <cstdio>
#include <algorithm>
#define M 51 //硬币最多50种
#define MAX_VALUE 10001 //金额最多1万
#define INF 0xffff0
using namespace std;
int mz[M],n,cnt=0;
int vis[MAX_VALUE];
int dfs(int amount){
	int minn=INF; //必须为局部变量,防止改变
    if(amount<0)
        return -1;
    if(amount==0){
        return 0;
    }
    if(vis[amount]>=0)
        return vis[amount];//记忆化搜索
    for(int i=0;i<n;i++){
        int k=dfs(amount-mz[i]);
        if(k!=-1)
        	minn=min(minn,k+1); //返回-1证明当前金额没有符合题意的解,minn会维持为INF方便下面返回,另:若有解要加上当前硬币
			//另:minn不能设为-1
    }
    if(minn!=INF)
    	vis[amount]=minn;  //必须在外面返回,否则循环执行一次就会终止,另外,如果minn为INF就证明当前金额没有符合题意的解,无需更新vis[amount]
    return vis[amount];
}

int main(){
    int t;
    fill(vis,vis+MAX_VALUE,-1); //一开始将所有值设为-1,如果没有解直接输出也是正确的
    scanf("%d%d",&t,&n);
    for(int i=0;i<n;i++)
        scanf("%d",&mz[i]);
    dfs(t);
    printf("%d",vis[t]);
    return 0;
}

3.动态规划
上面的递归保存了求得的值,属于动态规划的思想。但仍可以将递归改写为递推求解。
按照个人理解,递归是“自顶向下”将原问题“分解”,如本题中将总额t分解直至0,而动态规划则是“自底向上”将子问题“合成”为原问题的解。

#include <cstdio>
#include <cmath>
#include <algorithm>
#define M 51 //硬币最多50种
#define MAX_VALUE 10001 //金额最多1万
#define MAX 0xffff0
using namespace std;
int mz[M],k,cost[MAX_VALUE]; //mz代表面值,cost[i]为找零i元需要的硬币数最小值
void change(int x,int *used) {
    int i,j,k=-1;
    fill(cost,cost+M,MAX);
    int sum=0; //从1开始循环
    for(i=1; i<=x; i++) { //要找的钱数
        for(j=0; j<k; j++) {
            if(i>=mz[j]) {
                k=cost[i];
                cost[i]=min(cost[i],cost[i-mz[j]]+1); //状态转移方程,要+1选上当前物品
                if(k!=cost[i]) {
                    used[i]=mz[j];
                }
            }
        }
    }
    return cost;
}

int main() {
    int n,i/*,used[1000]= {0}*/; //used[i]为找零i最后找出的钱
    scanf("%d",&n);
//    int m=n;
	for(i=1;i<=n;i++)
		scanf("%d",&mz[i]);
    if(cost[n]==MAX)
        printf("-1");
    else {
        printf("%d",cost[n]);
/*        while(m>0) { //输出找零金额
            printf("%d ",used[m]);
            m-=used[m];
        }
        printf("\n");*/
    }
    return 0;
}
明日计划:0-1背包,完全背包问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值