long long fib(long long k){
if(k==1||k==2) return 1;
return fib(k-1)+fib(k-2);
}
上述函数存在一定的问题,比如 f i b ( n ) = f i b ( n − 1 ) + f i b ( n − 2 ) , f i b ( n − 1 ) = f i b ( n − 2 ) + f i b ( n − 2 ) fib(n)=fib(n-1)+fib(n-2),fib(n-1)=fib(n-2)+fib(n-2) fib(n)=fib(n−1)+fib(n−2),fib(n−1)=fib(n−2)+fib(n−2),其中 f i b ( n − 2 ) fib(n-2) fib(n−2)被计算了两次,层层递归下去,在 n n n很大时有大量重复计算,算法效率很低,可能导致递归爆栈。
我们只需要记录计算过的函数项,就可以提高算法效率。
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];
}
题目描述
树老师爬楼梯,有一楼梯共 n n n级,若每次只能跨上一级或者二级,要走上 n n n级,共有多少种不同走法?
例如:楼梯一共有 3 3 3级,他可以每次都走一级,或者第一次走一级,第二次走两级也可以第一次走两级,第二次走一级,一共 3 3 3种方法。
输入格式
输入包含若干行,每行包含一个正整数 N N N,代表楼梯级数, 1 ≤ N ≤ 30 1\le N\le 30 1≤N≤30
输出格式
不同的走法数,每一行输入对应一行输出。
样例输入
5
8
10
样例输出
8
34
89
题解
每次可以走 1 1 1步或 2 2 2步,所以走第 n n n步 ( n ≥ 3 ) (n\ge 3) (n≥3)时可以看做从 n − 1 n-1 n−1走 1 1 1步或从 n − 2 n-2 n−2走 2 2 2步,走第 n n n步方案数就等于前 2 2 2步方案数之和。
#include
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;
}
}
题目描述
有 1 ∗ n 1*n 1∗n的一个长方形,用一个 1 ∗ 1 1*1 1∗1、 1 ∗ 2 1*2 1∗2和 1 ∗ 3 1*3 1∗3的骨牌铺满方格。
例如当 n = 3 n=3 n=3时,共有 4 4 4种铺法。如下图:
输入格式
一个整数 n n n,表示 1 ∗ n 1*n 1∗n的长方形。
输出格式
一个整数表示方法总数。
样例输入
3
样例输出
4
数据范围与提示
1 < n < = 40 1
题解
每次可以铺 1 1 1格、 2 2 2格或 3 3 3格,所以铺 n n n格 ( n ≥ 4 ) (n\ge 4) (n≥4)时可以看做从 n − 1 n-1 n−1格、 n − 2 n-2 n−2格或 n − 3 n-3 n−3格开始铺,铺 n n n格方案数就等于 n − 1 n-1 n−1格、 n − 2 n-2 n−2格、 n − 3 n-3 n−3格方案数之和。
#include
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;
}
}
题目描述
H H H老师爬台阶,他可以每步上 1 1 1个或 2 2 2个台阶,输入台阶的级数 ,求不同的走法数。
例如: n = 3 n=3 n=3,台阶有 3 3 3个台阶,他可以每步爬 1 1 1个台阶,或者第 1 1 1步爬 1 1 1个台级,第 2 2 2步爬 2 2 2个台阶,也可以第 1 1 1步爬 2 2 2个台阶,第 2 2 2步爬 1 1 1个台阶,一共 3 3 3种爬法。
但不幸的是,台阶上有 k k k个台阶烂了, H H H老师不能踩在这些台阶上,现在给出台阶的级数 n n n和烂的 k k k个台阶,请你计算他上台阶的方法总数。
输入格式
第 1 1 1行是两个 n , k n,k n,k,代表台阶数和烂台阶的数目。
第 2 2 2行是 k k k个 1 ∼ n 1\sim n 1∼n的整数,表示烂台阶。
输出格式
不同的走法数。
样例输入
5 1
4
样例输出
3
数据范围与提示
100 % 的 数 据 满 足 : 1 ≤ n ≤ 60 , 0 ≤ k < n 100\%的数据满足:1\le n \le 60, 0\le k
题解
每次可以走 1 1 1步、 2 2 2步或 3 3 3步,所以走 n n n步 ( n ≥ 4 ) (n\ge 4) (n≥4)时可以看做从 n − 1 n-1 n−1步、 n − 2 n-2 n−2步或 n − 3 n-3 n−3步开始走,走 n n n步方案数就等于 n − 1 n-1 n−1步、 n − 2 n-2 n−2步、 n − 3 n-3 n−3步方案数之和,如果是烂台阶,则第 n n n步方案数位 0 0 0。
#include
#include
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;
}
题目描述
给定一个 n ∗ m n*m n∗m的矩阵,问从左上角的交叉点走到右下角的交叉点有多少条不同的路径(同一路径不允许重复走,也不允许往回走)。
输入格式
一行两个正整数 n , m n,m n,m
输出格式
路径数目 t t t
样例输入
6 4
样例输出
210
数据范围与提示
1 ≤ n ≤ 10 1 ≤ m ≤ 4 1\le n \le 10\\ 1\le m\le 4 1≤n≤101≤m≤4
题解
走到 ( i , j ) (i,j) (i,j)时,实际上是从 ( i − 1 , j ) (i-1,j) (i−1,j)或 ( i , j − 1 ) (i,j-1) (i,j−1)走过来的,所以路线数是从左或上走过来的路线数相加
#include
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];//输出走到终点的路线数
}
题目描述
一个 N × N N×N N×N的网格,你一开始在 ( 1 , 1 ) (1,1) (1,1),即左上角。每次只能移动到下方相邻的格子或者右方相邻的格子,问到达 ( N , N ) (N,N) (N,N),即右下角有多少种方法。
但是这个问题太简单了,所以现在有 M M M个格子上有障碍,即不能走到这 M M M个格子上。
输入格式
输入文件第 1 1 1行包含两个非负整数 N , M N, M N,M,表示了网格的边长与障碍数。
接下来 M M M行,每行两个不大于 N N N的正整数 x , y x,y x,y。表示坐标 ( x , y ) (x,y) (x,y)上有障碍不能通过,且有 1 ≤ x , y ≤ n 1≤x,y≤n 1≤x,y≤n,且 x , y x, y x,y至少有一个大于 1 1 1,并请注意障碍坐标有可能相同。
输出格式
一个非负整数,为答案 m o d 100003 mod 100003 mod100003后的结果。
输入样例
3 1
3 1
输出样例
5
数据范围与提示
对于 100 100% 100的数据,有 N ≤ 1000 , M ≤ 100000 N≤1000,M≤100000 N≤1000,M≤100000。
题解
解法类似于上题矩阵行走,区别就是如果 ( i , j ) (i,j) (i,j)是障碍物,则走到 ( i , j ) (i,j) (i,j)的路线数为 0 0 0
#include
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;
}
题目描述
杨辉三角形在组合数学中占有重要地位,与组合数、二项式定理等重要内容有关,如下图所示就是一个杨辉三角形:
通常用一个二维数组 C [ i ] [ j ] C[i][j] C[i][j]按右边示意图来存储杨辉三角形。 C [ i ] [ j ] C[i][j] C[i][j]表示第 i i i行第 j j j列的数字。
注意:行号从 0 0 0开始编号,列号也从 0 0 0开始编号。
输入格式
一行两个整数 i , j i,j i,j。
输出格式
输出 C [ i ] [ j ] C[i][j] C[i][j]的值,即杨辉三角形的第 i i i行第 j j j列的元素。
样例输入
5 3
样例输出
10
数据范围与提示
0 ≤ i ≤ 60 , 0 ≤ j ≤ i 0\le i\le 60, 0\le j\le i 0≤i≤60,0≤j≤i
题解
实际上杨辉三角形可以看作是一个下三角形矩阵,每行第一个和最后一个元素是1,中间的元素是上方元素与左上元素之和。
#include
#include
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
从第一行的数开始,每次可以往左下或右下走一格,直到走到最下行,把沿途经过的数全部加起来。如何走才能使得这个和尽量大?
输入格式
第一行输入整数 n n n表示三角形的层数。
在接下来的 n n n行中,每一行表示三角形的中每一行整数,整数之间以空格隔开。
输出格式
输出三角形从第一行的数到最后一行数所经过的数字之和的最大值。
样例输入
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
样例输出
30
数据范围与提示
1 ≤ n ≤ 500 − 10000 ≤ 三 角 形 中 的 整 数 ≤ 10000 1\le n\le 500\\ -10000\le 三角形中的整数\le 10000 1≤n≤500−10000≤三角形中的整数≤10000
题解
两种动态规划设计方式,设 n u m [ i ] [ j ] num[i][j] num[i][j]表示 ( i , j ) (i,j) (i,j)项的值
#include
#include
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
#include
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];
}
题目描述
设有由 n ( 1 ≤ n ≤ 1000 ) n(1\le n\le 1000) n(1≤n≤1000)个不相同的整数组成的数列,
记为 : b ( 1 ) 、 b ( 2 ) 、 … 、 b ( n ) b(1)、b(2)、\dots、b(n) b(1)、b(2)、…、b(n)若存在 i 1 < i 2 < ⋯ < i e i_1
程序要求,当原数列出之后,求出最长的不下降序列。
例如 13 , 7 , 9 , 16 , 38 , 24 , 37 , 18 , 44 , 19 , 21 , 22 , 63 , 15 13,7,9,16,38,24,37,18,44,19,21,22,63,15 13,7,9,16,38,24,37,18,44,19,21,22,63,15。
例中 13 , 16 , 18 , 19 , 21 , 22 , 63 13,16,18,19,21,22,63 13,16,18,19,21,22,63就是一个长度为 7 7 7的不下降序列,同时也有 7 , 9 , 16 , 18 , 19 , 21 , 22 , 63 7 ,9,16,18,19,21,22,63 7,9,16,18,19,21,22,63组成的长度为 8 8 8的不下降序列。
输入格式
第一行包含整数 N N N。
第二行包含 N N N个整数,表示完整序列。
输出格式
输出一个整数,表示最大长度。
输入样例
14
13 7 9 16 38 24 37 18 44 19 21 22 63 15
输出样例
8
数据范围与提示
1 ≤ N ≤ 1000 − 1 0 9 ≤ 数 列 中 的 数 ≤ 1 0 9 1\le N \le 1000\\ -10^9 \le 数列中的数 \le 10^9 1≤N≤1000−109≤数列中的数≤109
题解
用 d p [ i ] dp[i] dp[i]表示以下标 i i i结尾时最长上升长度,则 d p [ i ] = m a x ( d p [ j ] ) + 1 ( j < i , b [ j ] ≤ b [ i ] ) dp[i]=max(dp[j])+1(jdp[i]=max(dp[j])+1(j<i,b[j]≤b[i])
#include
#include
#include
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;
}
题目描述
给定两个长度分别为 N N N和 M M M的字符串 A A A和 B B B,求既是 A A A的子序列又是 B B B的子序列的字符串长度最长是多少。
输入格式
第一行包含两个整数 N N N和 M M M。
第二行包含一个长度为 N N N的字符串,表示字符串 A A A。
第三行包含一个长度为 M M M的字符串,表示字符串 B B B。
字符串均由小写字母构成。
输出格式
输出一个整数,表示最大长度。
输入样例
4 5
acbd
abedc
输出样例
3
数据范围与提示
1 ≤ N ≤ 1000 1\le N \le 1000 1≤N≤1000
题解
用 d p [ i ] [ j ] dp[i][j] dp[i][j]表示 A , B A,B A,B分别取前 i , j i,j i,j长度时最长公共子序列长度。
显然 A [ i ] = = B [ j ] A[i]==B[j] A[i]==B[j]时 d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] + 1 dp[i][j]=dp[i-1][j-1]+1 dp[i][j]=dp[i−1][j−1]+1, A [ i ] ≠ B [ j ] A[i]\ne B[j] A[i]=B[j]时 d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] ) dp[i][j]=max(dp[i-1][j],dp[i][j-1]) dp[i][j]=max(dp[i−1][j],dp[i][j−1])
#include
#include
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;
}
题目描述
有 N N N件物品和一个容量是 V V V的背包。每件物品只能使用一次。
第 i i i件物品的体积是 v i v_i vi,价值是 w i w_i wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行两个整数, N , V N,V N,V,用空格隔开,分别表示物品数量和背包容积。
接下来有 N N N行,每行两个整数 v i , w i v_i,w_i vi,wi,用空格隔开,分别表示第 i i i件物品的体积和价值。
题解
用 d p [ i ] [ j ] dp[i][j] dp[i][j]表示取前 i i i个物品装入最大容量为 j j j的背包所获得最大价值。
#include
#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;
}
题目描述
有 N N N种物品和一个容量是 V V V的背包,每种物品都有无限件可用。
第 i i i种物品的体积是 v i v_i vi,价值是 w i w_i wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行两个整数 N , V N,V N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N N N行,每行两个整数 v i , w i v_i,w_i vi,wi,用空格隔开,分别表示第 i i i种物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
输入样例
4 5
1 2
2 4
3 4
4 5
输出样例
10
数据范围与提示
0 < N , V ≤ 10000 < v i , w i ≤ 1000 0
题解
以01背包问题为基础,用 d p [ i ] [ j ] dp[i][j] dp[i][j]表示取前 i i i个物品装入最大容量为 j j j的背包所获得最大价值。
j − k ∗ v [ i ] > = 0 j-k*v[i]>=0 j−k∗v[i]>=0表示能装下 k k k个物品 i i i, d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j − k ∗ v [ i ] ] + k ∗ w [ i ] ) dp[i][j]=max(dp[i-1][j-k*v[i]]+k*w[i]) dp[i][j]=max(dp[i−1][j−k∗v[i]]+k∗w[i])
#include
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];
}