动态规划——填表
#include<bits/stdc++.h>
using namespace std;
int DP[1010][1010];
int n,m;
int main(){
DP[0][0]=1;
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
DP[i][j]=DP[i-1][j]+DP[i-1][j-1];
}
}
cout<<DP[n][m];
return 0;
}
最长上升子序列问题求解
上升子序列指的是对于任意的i<j都满足ai<aj的子序列。
例如:序列为(1,5 ,2,6,9,10,3,15),那么它的最长上升子序列为:(1,2,6,9,10,15)
求n个数的最长上升子序列,可以求前n-1个数的最长上升子序列,再跟第n个数进行判断。求前n-1个数的最长上升子序列,可以通过求前n-2个数的最长上升子序列……直到求前1个数的最长上升子序列,此时LIS的长度当然为1。
让我们举个例子:求 2 7 1 5 6 4 3 8 9 的最长上升子序列。我们定义d(i) (i∈[1,n])来表示前i个数以A[i]结尾的最长上升子序列长度。
前1个数 d(1)=1 子序列为2;
前2个数 7前面有2小于7 d(2)=d(1)+1=2 子序列为2 7
前3个数 在1前面没有比1更小的,1自身组成长度为1的子序列 d(3)=1 子序列为1
前4个数 5前面有2小于5 d(4)=d(1)+1=2 子序列为2 5
前5个数 6前面有2 5小于6 d(5)=d(4)+1=3 子序列为2 5 6
前6个数 4前面有2小于4 d(6)=d(1)+1=2 子序列为2 4
前7个数 3前面有2小于3 d(3)=d(1)+1=2 子序列为2 3
前8个数 8前面有2 5 6小于8 d(8)=d(5)+1=4 子序列为2 5 6 8
前9个数 9前面有2 5 6 8小于9 d(9)=d(8)+1=5 子序列为2 5 6 8 9
d(i)=max{d(1),d(2),……,d(i)} 我们可以看出这9个数的LIS为d(9)=5
总结一下,d(i)就是找以A[i]结尾的,在A[i]之前的最长上升子序列+1,当A[i]之前没有比A[i]更小的数时,d(i)=1。所有的d(i)里面最大的那个就是最长上升子序列。其实说的通俗点,就是每次都向前找比它小的数和比它大的数的位置,将第一个比它大的替换掉,这样操作虽然LIS序列的具体数字可能会变,但是很明显LIS长度还是不变的,因为只是把数替换掉了,并没有改变增加或者减少长度。但是我们通过这种方式是无法求出最长上升子序列具体是什么的,这点和最长公共子序列不同。
序列:(7, 9, 6, 10, 7, 1, 3)分别为 (a1, a2, a3, a4, a5, a6, a7)
我们定义dp[i] (i∈[1,n])来表示前i个数以ai结尾的最长上升子序列长度。
最开始a1 = 7, 令dp[ 1 ] = 1;
然后看下一个元素 a2 = 9, 令dp[ 2 ] = 1, 那么需要检查 i 前面是否有比他小的 因为 a1 < a2 而且 dp[ 1 ] + 1 > dp[ 2 ], 所以dp[ 2 ] = dp[ 1 ] + 1 == 2;
然后再看下一个元素 a3 = 6, 令 dp[ 3 ] = 1, 那么需要检查前面的元素 a1 与 a2 是否有比他小的, 一看没有,那么 到目前为止,子序列就是他自己。
然后再看一下下一个元素 a4 = 10; 令 dp[ 4 ] = 1; 那么需要依次检查前面的元素 a1 与 a2 与 a3 是否有比他小的 , 一看a1比它小,而且呢,dp[ 1 ] + 1 > dp[ 4 ] 所以呢 dp[ 4 ] = dp[ 1 ] + 1 == 2, 说明此时 a1 与 a4 可以构成一个长度为2 的上升子序列,再来看看还可不可以构成更长的子序列呢,所以咱们再来看看 a2 , a2 < a4 而且呢 dp[ 2 ] + 1 == 3 > dp[ 4 ] == 2 所以呢dp[ 4 ] = dp[ 2 ] + 1 == 3, 因为 和 a2 构成序列的是 a1 a2 , 那么此时的序列应该为:a1 a2 a4 ;然后再来看 a3 , a3 < a4 但是可惜的是dp[ 3 ] + 1 == 2 < dp[ 4 ] == 3 。
a5=7, 令dp[5]=1, a3<a5, dp[3]+1=2>dp[5], dp[5]=dp[3]+1=2, 由此可以看出, a3, a5构成一个长度为2的上升子序列;其余元素a1, a2, a4都不小于a5。
然后就是重复上诉过程,找到最大的dp [ i ] 那么这个数就是最长上升子序列。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int main()
{
int arr[500], n, dp[500], ans = -1;
scanf("%d", &n);
for(int i = 1; i <= n; i++)
scanf("%d", &arr[i]); //不建议用cin cout 他们执行的时候还得先分析数据类型,耗时比 scanf printf 多好多,很多题目就因为这个地方而超时,导致比赛的时候罚时
/* 求解最长子序列的个数的核心代码 */
/* ********************************************** */
for(int i = 1; i <= n; i++){
dp[i] = 1; //初始化
for(int j = 1; j < i; j++){
if(arr[j] < arr[i]) // 如果求最大下降子序列则反之
dp[i] = max(dp[i], dp[j] + 1);
}
ans = max(dp[i], ans);
}
/* ********************************************** */
printf("最长子序列的个数为: %d", ans);
return 0;
}
/*
样例:
7
7 9 6 10 7 1 3
最长子序列的个数为: 3
*/