练习17,小y拼楼梯【动态规划DP/贪心】

题源:E-⼩y拼楼梯 - 问题 - USCOJ(PvP)

题目描述

⾃从⼩y上次学会计算有多少种爬楼梯的⽅式后,又枯燥了起来。现在她⾃⼰拼接⾃⼰想要的楼梯,于是她就开始⾃⼰脑补拼接楼梯了。她在想,⾃⼰拥有⽆限个分别为 a1,a2,a3……ak 阶的⼩楼梯,她想拼出⼀个 N 阶的楼梯最少需要多少块?

例如⼩y现在有分别为 1,2,4 阶的⼩楼梯,想要拼出 10 阶的楼梯⾄少需要 3 块⼩楼梯,分别是 4 阶的⼩楼梯使⽤ 2 块,2 阶的⼩楼梯使⽤ 1 块,即 4+4+2=10

但是⼩y现在想破了脑袋也没想到怎么计算最少值,你能帮帮他吗?
如果⽆法拼出 N 阶则输出 "-_-!!"

输入

第⼀⾏包含两个正整数 N, k    N表⽰⼩y最要拼接的楼梯阶数,表⽰⼩楼梯的个数。

第⼆⾏包含 k 个正整数 a1,a2,a3……ak(ai < aj,其中i < j),表⽰第 i 个⼩楼梯的阶数。

输出

如果能拼接出来,则输出最少需要的⼩楼梯块数。

否则输出 "-_-!!" ,(不输出引号)。

对于30%的数据:

1e9≤n≤1e16k≤10 ,且这个范围的数据对于所有ai(i>1) ,都有 a[i] 是 a[i-1] 的倍数。

对于另外 70% 的数据中:
⼀半(35%)的数据: N≤1000,k≤5
全部(70%)的数据: N≤100000,k≤20

对于所有数据 a[i]≤N

思路

对30%的数据,因为a[i] 是 a[i-1] 的倍数,所以用贪心的方法即最优解法

而对剩下70%的数据,则需要动态规划做

代码如下:

#include<bits/stdc++.h>
using namespace std;
#define lint long long

lint n,k,f=0;
lint a[100],d[100010]={0};
lint dp(int m){ //动态规划
    int flag=0; //flag来标记无法被任何已有小楼梯所拼出的阶数
    if(m==0) {f=1; return 0;} //f=1表示能拼出目标楼梯数
    if(d[m]) return d[m]; //记忆化搜索
    for(int i=1;i<=k;i++){ //从k个小楼梯中递归找出全局最优解
        if(m-a[i]>=0){
            flag=1;
            if(d[m]==0) d[m]=dp(m-a[i])+1;
            else d[m]=min(d[m],dp(m-a[i])+1);
        }
    }
    if(flag==0) return 1e9;
    return d[m];
}
int main(){
    cin >> n >> k;
    for(int i=1;i<=k;i++) cin >> a[i];

    if(n>=1e9){ //贪心
        lint ans=0,i=k;
        while(n>0 && i>0){
            ans+=n/a[i];
            n%=a[i];
            i--;
        }
        if(n!=0) cout << "-_-!!";
        else cout << ans;
        return 0;
    }

    lint ans=dp(n);
    if(f==1) cout << ans;
    else cout << "-_-!!";
    return 0;
}

此题动态规划还有一种递推方法(从0阶递推到n阶小楼梯):

#include<bits/stdc++.h>
using namespace std;
#define lint long long

lint a[100],d[100010]={0};
int main(){
    lint n,k;
    cin >> n >> k;
    for(int i=1;i<=k;i++) cin >> a[i];
    d[0]=0;
    for(int i=1;i<=n;i++){
        lint cost=1e16;
        for(int j=1;j<=k;j++)
            if(i-a[j]>=0) cost=min(cost,d[i-a[j]]+1);
        d[i]=cost;
    }
    for(int i=1;i<=n;i++) cout << d[i] << endl;
    cout << d[n];
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ILECY

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值