本文已参与「新人创作礼」活动,一起开启掘金创作之路。
【C语言】动态规划法的设计与实现
一、目的
掌握动态规划法的设计思想、具体实现和时间复杂度分析。
二、实验内容
先用伪代码或流程图描述利用动态规划解决的算法解决方案,再用程序实现,计算时间复杂度,记录最终测试数据和测试结果。
2.1实验内容:
2.1.1 斐波那契数列第n项的值。(自顶向下备忘 , 自底向上推导)
2.1.2 找零钱问题
2.1.3 多源点最短路径问题。
2.1.4爬台阶问题(设每次只能走一级或者两级台阶,请问走到第n级台阶一共有多少总走法)
三、设计和编码
3.1算法设计
3.1.1找零钱问题(P125,第8题)
输入:需要找的零钱
输出:零钱
1.定义ins=0,零钱种类change[]={100,50,20,10,5,2,1}
2.while(n > 0)
3.ans = n/change[i]
4.if(ans!=0)
5.sum_ans = sum_ans + ans
6.n = n - ans * change[i]
3.1.2多源点最短路径问题。
输入:点与边的关系
输出:最短路径
1.For k:=1 to n
2.For i:=1 to n
3.For j:=1 to n
3.1 If D[i,j]>D[i,k]+D[k,j] Then
3.1.1 D[i,j]:=D[i,k]+D[k,j];
3.1.3爬台阶问题(设每次只能走一级或者两级台阶,请问走到第n级台阶一共有多少总走法)
输入:要爬/跳的台阶数
输出:爬/跳法
1.若n==3,返回n,否则返回 Frog_jump(n - 2) + Frog_jump(n - 1);
2.输出ret;
3.2程序代码
3.2.1.斐波那契数列第n项的值。(自顶向下备忘 , 自底向上推导)
3.2.1.1自顶向下
#include<iostream>
#include<cstdio>
using namespace std;
int F[1000]={0};
int f(int x){
if(x==0){F[0]=1;return F[0];}
if(x==1){F[1]=1;return F[1];}
if(F[x])return F[x];//避免重复计算,有值立即返回
else{
F[x]=f(x-1)+f(x-2);
return F[x];
}
}
int main(){
int n;
scanf("%d",&n);
f(n);
printf("F(%d)=%d\n",n,F[n]);
}
3.2.1.2自底向上
#include<iostream>
#include<cstdio>
using namespace std;
int F[1000000];
int main(){
int n;
scanf("%d",&n);
F[0]=1;F[1]=1;
for(int i=2;i<=n;i++){
F[i]=F[i-1]+F[i-2];
}
printf("F(%d)=%d\n",n,F[n]);
}
3.2.2.找零钱问题
#include <stdio.h>
int Coin(double m)
{
printf("=====找零钱的方案为=====\n");
int ans = 0;
int n;
n = (int)(m*100);
int change[]={100,50,20,10,5,2,1};
int i = 0;
int sum_ans = 0;
while(n > 0)
{
ans = n / change[i];
if(ans != 0)
{
sum_ans = sum_ans + ans;
printf("需要找面额为%.2lf元的硬币个数为%d\n",1.0 * change[i] / 100, ans);
}
n = n - ans * change[i];
i++;
}
printf("需要找零的硬币总数为%d\n", sum_ans);
}
int main()
{
double n; printf("请输入需要找给顾客的钱(单位:元、精确度为0.01):\n");
scanf("%lf",&n);
if((int)n*100 == 0)
{
printf("不需要找零钱\n");
}
else
{
Coin(n);
}
return 0;
}
3.2.3.多源点最短路径问题
#include <iostream>
#include <cstring>
#include <string>
using namespace std;
long long dist[102][102]; //表示最大节点数是101个
long long minNum(long long a, long long b){
if(a == -1) return b;
return a < b ? a : b;
}
void floyd(long long dist[][102], int N){
for(int i = 1; i <= N; i++){
for(int j = 1; j <= N; j++){
for(int k = 1; k <= N; k++){
if(dist[j][i] == -1 || dist[i][k] == -1){
continue;//如果没路径别走
}
dist[j][k] = minNum(dist[j][k], dist[j][i]+dist[i][k]);
}
}
}
}
int main(){
int N,M;
while(cin>>N>>M){
for(int i = 0; i <= N; i++){
for(int j = 0; j <= N; j++){
dist[i][j] = -1; //初始化为-1 表示无穷远
if(i == j){
dist[i][j] = 0;
}
}
}
int x, y, d;
for(int i = 0; i < M; i++){
cin>>x>>y>>d;
dist[x][y] = minNum(dist[x][y], d);
dist[y][x] = dist[x][y];//无向图,如果是有向图去掉这个路径
}
floyd(dist, N);
for(int i = 1; i <= N; i++){ //输出整个矩阵
for(int j = 1; j<= N; j++){
cout<<dist[i][j]<<" ";
}
cout<<endl;
}
}
return 0;
}
3.2.4.爬台阶问题(设每次只能走一级或者两级台阶,请问走到第n级台阶一共有多少总走法)
#include <stdio.h>
#include <stdlib.h>
int Frog_jump(int n)
{
if (n < 3)
return n;
else
return Frog_jump(n - 2) + Frog_jump(n - 1);
}
int main()
{
int n = 0;
int ret = 0;
printf("请输入青蛙要跳的台阶数:");
scanf("%d", &n);
ret = Frog_jump(n);
printf("有%d种跳法\n", ret);
system("pause");
return 0;
}
四、运行结果及分析
4.1运行结果
4.1.1斐波那契数列第n项的值。(自顶向下备忘 , 自底向上推导)
时间复杂度:O(n)
4.1.2找零钱问题(P125,第8题)
时间复杂度:O(n)
4.1.3多源点最短路径问题
时间复杂度:O(n^2)
4.1.4爬台阶问题(设每次只能走一级或者两级台阶,请问走到第n级台阶一共有多少总走法)
时间复杂度:O(n)
4.2分析
用动态规划法进行程序设计往往是针对一种最优化问题,由于各种问题的性质不同,确定最优解的条件也互不相同,因而动态规划的设计方法对不同的问题,有各具特色的解题方法,而不存在一种万能的动态规划算法,可以解决各类最优化问题。
五、实验小结
动态规划程序设计是对解最优化问题的一种途径、一种方法,而不是一种特殊算法。不像搜索或数值计算那样,具有一个标准的数学表达式和明确清晰的解题方法。因此在学习该方法时,除了要对基本概念和方法正确理解外,必须具体问题具体分析处理,以丰富的想象力去建立模型,用创造性的技巧去求解。
我们也可以通过对若干有代表性的问题的动态规划算法进行分析、讨论,逐渐学会并掌握这一设计方法我们分析并解决了所有必做任务,并且对选做任务中的多个算法进行了简易的分析,并且计算出各个任务的时间复杂度。