斐波那契
递归式斐波那契函数
long long fib(long long k){
if(k==1||k==2) return 1;
return fib(k-1)+fib(k-2);
}
上述函数存在一定的问题,比如,其中被计算了两次,层层递归下去,在很大时有大量重复计算,算法效率很低,可能导致递归爆栈。
记忆化优化递归式斐波那契函数
我们只需要记录计算过的函数项,就可以提高算法效率。
long long data[101];//可记录斐波那契1~100项的值
long long fib(long long k){
if(data[k]!=0) return data[k];//保存过函数值直接返回
if(k==1||k==2) data[k]=1;
else data[k]=fib(k-1)+fib(k-2);
return data[k];//返回上两行计算的函数值
}
递推式斐波那契函数
我们可以去掉递归,直接使用数组计算斐波那契函数
long long fib[101];//可记录斐波那契1~100项的值
long long init(){
//fib[k]记录的是斐波那契第k项,使用fib数组要先调用init初始化fib数组
fib[1]=fib[2]=1;
for(int i=3;i<=100;i++) fib[i]=fib[i-1]+fib[i];
}
基础动态规划
爬楼梯
题目描述
树老师爬楼梯,有一楼梯共级,若每次只能跨上一级或者二级,要走上级,共有多少种不同走法? 例如:楼梯一共有级,他可以每次都走一级,或者第一次走一级,第二次走两级也可以第一次走两级,第二次走一级,一共种方法。
输入格式
输入包含若干行,每行包含一个正整数,代表楼梯级数,
输出格式
不同的走法数,每一行输入对应一行输出。
样例输入
5
8
10
样例输出
8
34
89
题解
每次可以走步或步,所以走第步时可以看做从走步或从走步,走第步方案数就等于前步方案数之和。
#include <iostream>
using namespace std;
int main(){
long long num[31];
num[1]=1;
num[2]=2;
for(int i=3;i<=30;++i) num[i]=num[i-1]+num[i-2];
int n;
while(cin>>n){
cout<<num[n]<<endl;
}
}
骨牌铺法
题目描述
有的一个长方形,用一个 、和的骨牌铺满方格。
例如当时,共有种铺法。如下图:
输入格式
一个整数,表示的长方形。
输出格式
一个整数表示方法总数。
样例输入
3
样例输出
4
数据范围与提示
题解
每次可以铺格、格或格,所以铺格时可以看做从格、格或格开始铺,铺格方案数就等于格、格、格方案数之和。
#include <iostream>
using namespace std;
int main(){
long long num[41];
num[1]=1;
num[2]=2;
num[3]=4;
for(int i=4;i<=40;++i) num[i]=num[i-1]+num[i-2]+num[i-3];
int n;
while(cin>>n){
cout<<num[n]<<endl;
}
}
爬台阶
题目描述
老师爬台阶,他可以每步上个或个台阶,输入台阶的级数 ,求不同的走法数。
例如:,台阶有个台阶,他可以每步爬个台阶,或者第步爬个台级,第步爬个台阶,也可以第步爬个台阶,第步爬个台阶,一共种爬法。
但不幸的是,台阶上有个台阶烂了,老师不能踩在这些台阶上,现在给出台阶的级数和烂的个台阶,请你计算他上台阶的方法总数。
输入格式
第行是两个,代表台阶数和烂台阶的数目。
第行是个的整数,表示烂台阶。
输出格式
不同的走法数。
样例输入
5 1
4
样例输出
3
数据范围与提示
题解
每次可以走步、步或步,所以走步时可以看做从步、步或步开始走,走步方案数就等于步、步、步方案数之和,如果是烂台阶,则第步方案数位。
#include <iostream>
#include <algorithm>
using namespace std;
int main(){
int n,k,p;
long long num[101];
num[0]=1;
cin>>n>>k;
for(int i=0;i<k;i++){
cin>>p;
num[p]=-1;//标记p为烂台阶
}
for(int i=1;i<=n;i++){
if(i==1){
if(num[i]==-1) num[i]=0;//烂台阶
else num[1]=1;
}else{
if(num[i]==-1) num[i]=0;//烂台阶
else num[i]=num[i-1]+num[i-2];
}
}
if(num[n]==-1) printf("%lld\n",num[n-1]);
else printf("%lld\n",num[n]);
return 0;
}
二维动态规划
矩阵行走
题目描述
给定一个的矩阵,问从左上角的交叉点走到右下角的交叉点有多少条不同的路径(同一路径不允许重复走,也不允许往回走)。
输入格式
一行两个正整数
输出格式
路径数目
样例输入
6 4
样例输出
210
数据范围与提示
题解
走到时,实际上是从或走过来的,所以路线数是从左或上走过来的路线数相加
#include <iostream>
using namespace std;
int main(){
long long num[11][5];//记录走到(i,j)时路线数
int n,m;
cin>>n>>m;
//走第一行第一列都是1条路线
for(int j=0;j<=m;j++) num[0][j]=1;
for(int i=0;i<=n;i++) num[i][0]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
num[i][j]=num[i-1][j]+num[i][j-1];//可以从左或上走,方案数是左和上之和
}
cout<<num[n][m];//输出走到终点的路线数
}
走格子
题目描述
一个的网格,你一开始在,即左上角。每次只能移动到下方相邻的格子或者右方相邻的格子,问到达,即右下角有多少种方法。
但是这个问题太简单了,所以现在有个格子上有障碍,即不能走到这个格子上。
输入格式
输入文件第行包含两个非负整数,表示了网格的边长与障碍数。
接下来行,每行两个不大于的正整数。表示坐标上有障碍不能通过,且有,且至少有一个大于,并请注意障碍坐标有可能相同。
输出格式
一个非负整数,为答案后的结果。
输入样例
3 1
3 1
输出样例
5
数据范围与提示
对于的数据,有。
题解
解法类似于上题矩阵行走,区别就是如果是障碍物,则走到的路线数为
#include<iostream>
using namespace std;
const int MAXN=1005;//数组大小
const int MOD=100003;//模
int n,m;
int a[MAXN][MAXN];//递推数组
bool flg[MAXN][MAXN];//标记数组
int main(){
cin>>n>>m;//输入
while(m--){
int x,y;
cin>>x>>y;
flg[x][y]=1;//标记
}
a[1][1]=1;//初始值为1
for(int i=1;i<=n;i++){//两层循环枚举方格
for(int j=1;j<=n;j++){//同上
a[i][j]+=a[i-1][j]+a[i][j-1];//递推式
if(flg[i][j]==1) a[i][j]=0;//判断
a[i][j]=a[i][j]%MOD;//模100003
}
}
cout<<a[n][n];//输出路径总数
return 0;
}
杨辉三角形
题目描述
杨辉三角形在组合数学中占有重要地位,与组合数、二项式定理等重要内容有关,如下图所示就是一个杨辉三角形:
通常用一个二维数组按右边示意图来存储杨辉三角形。表示第行第列的数字。 注意:行号从开始编号,列号也从开始编号。
输入格式
一行两个整数。
输出格式
输出的值,即杨辉三角形的第行第列的元素。
样例输入
5 3
样例输出
10
数据范围与提示
题解
实际上杨辉三角形可以看作是一个下三角形矩阵,每行第一个和最后一个元素是1,中间的元素是上方元素与左上元素之和。
#include <iostream>
#include <algorithm>
using namespace std;
const int N=62;
const int MOD=100003;
int main(){
long long num[N][N];
num[0][0]=num[1][0]=num[1][1]=1;
int n,m;
cin>>n>>m;
for(int i=2;i<=n;i++){
num[i][0]=1;
for(int j=1;j<=i-1;j++){
num[i][j]=num[i-1][j]+num[i-1][j-1];
}
num[i][i]=1;
}
cout<<num[n][m];
}
动态规划-数字三角形模型
数字三角形
题目描述
有一个由非负整数组成的三角形,第一行只有一个数,除了最下行之外每个数的左下方和右下方各有一个数。
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
从第一行的数开始,每次可以往左下或右下走一格,直到走到最下行,把沿途经过的数全部加起来。如何走才能使得这个和尽量大?
输入格式
第一行输入整数表示三角形的层数。
在接下来的行中,每一行表示三角形的中每一行整数,整数之间以空格隔开。
输出格式
输出三角形从第一行的数到最后一行数所经过的数字之和的最大值。
样例输入
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
样例输出
30
数据范围与提示
题解
两种动态规划设计方式,设表示项的值
- 表示从走到的最大值,
#include <iostream>
#include <algorithm>
using namespace std;
#define N 1001
int dp[N][N];
int main(){
int n;
int ret;
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
cin>>dp[i][j];
if(j==1) dp[i][j]+=dp[i-1][j];
else if(j==i) dp[i][j]+=dp[i-1][j-1];
else dp[i][j]+=max(dp[i-1][j-1],dp[i-1][j]);
if(i==n){
if(j==1) ret=dp[i][j];
else ret=max(ret,dp[i][j]);
}
}
}
cout<<ret;
}
- 表示从最后一行走到的最大值,
#include <iostream>
#include <algorithm>
using namespace std;
#define N 1001
int dp[N][N];
int main(){
int n;
int ret;
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
cin>>dp[i][j];
}
}
for(int i=n-1;i>=1;i--){
for(int j=1;j<=i;j++){
dp[i][j]+=max(dp[i+1][j],dp[i+1][j+1]);
}
}
cout<<dp[1][1];
}
动态规划-最长上升子序列模型
最长上升子序列
题目描述
设有由个不相同的整数组成的数列,
记为 : 若存在且有则称为长度为的不下降序列。
程序要求,当原数列出之后,求出最长的不下降序列。
例如。
例中就是一个长度为的不下降序列,同时也有组成的长度为的不下降序列。
输入格式
第一行包含整数。
第二行包含个整数,表示完整序列。
输出格式
输出一个整数,表示最大长度。
输入样例
14
13 7 9 16 38 24 37 18 44 19 21 22 63 15
输出样例
8
数据范围与提示
题解
用表示以下标结尾时最长上升长度,则
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
#define N 1001
int dp[N];
int num[N];
int main() {
int n, mLen = 0;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> num[i];
}
for (int i = 0; i < n; i++) {
dp[i] = 1;
for (int j = 0; j < i; j++) {
if (num[j] <= num[i]) {
dp[i] = max(dp[i], dp[j] + 1);
}
}
mLen = max(mLen, dp[i]);
}
cout << mLen;
}
最长公共子序列
题目描述
给定两个长度分别为和的字符串和,求既是的子序列又是的子序列的字符串长度最长是多少。
输入格式
第一行包含两个整数和。
第二行包含一个长度为的字符串,表示字符串。
第三行包含一个长度为的字符串,表示字符串。
字符串均由小写字母构成。
输出格式
输出一个整数,表示最大长度。
输入样例
4 5
acbd
abedc
输出样例
3
数据范围与提示
题解
用表示分别取前长度时最长公共子序列长度。 显然时,时
#include <iostream>
#include <string>
using namespace std;
#define N 1001
int dp[N][N];
char s1[N],s2[N];
int main()
{
int n1,n2;
scanf("%d%d%s%s",&n1,&n2,s1+1,s2+1);
//+1是因为保留下标0
for(int i=0;i<=n1;i++){
for(int j=0;j<=n2;j++){
if(i==0||j==0){
dp[i][j]=0;//有下标0是空串
continue;
}
if(s1[i]==s2[j]) dp[i][j]=dp[i-1][j-1]+1;
else{
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
}
}
printf("%d",dp[n1][n2]);
return 0;
}
动态规划-背包问题
01背包
题目描述
有件物品和一个容量是的背包。每件物品只能使用一次。
第件物品的体积是,价值是。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式 第一行两个整数,,用空格隔开,分别表示物品数量和背包容积。
接下来有行,每行两个整数,用空格隔开,分别表示第件物品的体积和价值。
题解 用表示取前个物品装入最大容量为的背包所获得最大价值。
- 表示能装下物品,,的两项分别表示装货不装物品
- 表示不能装下物品,
#include <iostream>
#define maxL 1001
using namespace std;
int main(){
int dp[maxL][maxL],v[maxL],w[maxL];
//dp[i][j]表示选取i号背包使用j空间的最大价值
int n,maxW;
cin>>n>>maxW;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
for(int i=0;i<=n;i++){
for(int j=0;j<=maxW;j++){
if(i==0||j==0) dp[i][j]=0;
else if(j<v[i]) dp[i][j]=dp[i-1][j];
else dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);
}
}
cout<<dp[n][maxW];
return 0;
}
完全背包
题目描述
有种物品和一个容量是的背包,每种物品都有无限件可用。
第种物品的体积是,价值是。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。 输出最大价值。
输入格式
第一行两个整数,用空格隔开,分别表示物品种数和背包容积。
接下来有行,每行两个整数,用空格隔开,分别表示第种物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
输入样例
4 5
1 2
2 4
3 4
4 5
输出样例
10
数据范围与提示
题解 以01背包问题为基础,用表示取前个物品装入最大容量为的背包所获得最大价值。 表示能装下个物品,
#include <iostream>
using namespace std;
#define N 10001
int dp[N][N];
int v[N],w[N];
int n,V;
int main(){
cin>>n>>V;
for(int i=1;i<=n;i++){
cin>>v[i]>>w[i];//输入
}
for(int i=1;i<=n;i++){
for(int j=1;j<=V;j++){
for(int k=0;k*v[i]<=j;k++){
dp[i][j]=max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);
//求如何装入可得到最大的dp[i][j]
}
}
}
cout<<dp[n][V];
}
版权声明
- 本文档归cout0所有,仅供学习使用,未经允许,不得转载。