题目描述
⾃从⼩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 表⽰⼩楼梯的个数。
第⼆⾏包含 k 个正整数 a1,a2,a3……ak(ai < aj,其中i < j),表⽰第 i 个⼩楼梯的阶数。
输出
如果能拼接出来,则输出最少需要的⼩楼梯块数。
否则输出 "-_-!!" ,(不输出引号)。
对于30%的数据:
1e9≤n≤1e16,k≤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;
}