动态规划基础问题
动态规划是优化的暴力,决策结果构成状态空间。动态规划的状态空间的遍历就是一张有向无环图,当前状态递推其后状态。当某一个状态确定后,不再受其后状态影响。
题目实现代码简单,难点在于状态表示、决策的状态转移方程构造保证无后效性且合理,有时边界处理不当容易出错。在做题时可以输出状态空间验证决策是否正确。
最长上升序列LIS && 最长不增序列
洛谷导弹拦截
理解题目求解的是 最长不增序列&& 最长上升序列LIS
#include<bits/stdc++.h>
using namespace std;
#define N 100010
int n,a[N];
int dpup[N],cntup,dplow[N],cntlow;
bool cmp(int x,int y){
return x>y;
}
void solve(int a[]){
cntlow=1;cntup=1;
dplow[1]=a[1];
dpup[1]=a[1];
for(int i=2;i<=n;i++){
//维护单调不增序列
//小于等于末尾元素直接加入
//不增即可以有相同元素出现,整体在减小,选择位置时不应该替代相同元素
if(dplow[cntlow]>=a[i]) dplow[++cntlow]=a[i];
else{
int p=upper_bound(dplow+1,dplow+1+cntlow,a[i],cmp)-dplow;
dplow[p]=a[i];
}
//维护单调递增序列
//大于队尾元素直接插入
//递增不允许相同元素出现,选择位置时可替代相同元素
if(dpup[cntup]<a[i]) dpup[++cntup]=a[i];
else{
int p=lower_bound(dpup+1,dpup+1+cntup,a[i])-dpup;
dpup[p]=a[i];
}
/*
printf("不增\t");
for(int i=1;i<=cntlow;i++) cout<<dplow[i]<<' ';
cout<<endl;
printf("递增\t");
for(int i=1;i<=cntup;i++) cout<<dpup[i]<<' ';
cout<<endl;
*/
}
cout<<cntlow<<endl<<cntup<<endl;
}
int main()
{
while(scanf("%d",&a[++n])!=EOF);
n--;
solve(a);
}
/*
void test(){
int b[]={0,1,2,3,3,5,7};
cout<<upper_bound(b+1,b+6,2)-b<<endl;
cout<<upper_bound(b+1,b+6,4)-b<<endl;
cout<<upper_bound(b+1,b+6,5)-b<<endl;
cout<<upper_bound(b+1,b+6,8)-b<<endl;
//输出 3 5 6 6
//返回大于const的第一个数的指针位置
//最后大于最后一个仍然返回最后一个
}
/*
void test(){
int b[]={0,1,2,3,3,5};
cout<<lower_bound(b+1,b+5,3)-b<<endl;
cout<<lower_bound(b+1,b+5,4)-b<<endl;
cout<<lower_bound(b+1,b+5,5)-b<<endl;
cout<<lower_bound(b+1,b+5,6)-b<<endl;
//输出 3 5 5 5
//返回大于等于const的第一个一个
}
*/
LCS
LCS 最大公共子序列,求解两个序列P1,P2 ,求它们的最长公共子序列。
暴力解法
#include<bits/stdc++.h>
using namespace std;
#define N 1010
int s1[N],s2[N],n;
int dp[N][N];
void LCS(int s1[],int s2[])
{
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
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]);
}
}
}
cout<<dp[n][n]<<endl;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>s1[i];
for(int i=1;i<=n;i++) cin>>s2[i];
LCS(s1,s2);
}
洛谷最大公共子序列题目,给的两个数列都是全排列,既保证了数据范围不会太大,又保证了子序列元素的唯一性,数列a中的元素必定会在数列b中出现。
如果将a数组映射到b数组,在b数组中仍能保持原顺序的部分就是最大公共子序列。
#include<bits/stdc++.h>
using namespace std;
#define N 101000
int s1[N],s2[N],n;
int dp[N],cnt,m[N];
void LIS(int s2[])
{
//最长单调上升子序列
dp[1]=s2[1];
cnt=1;
for(int i=2;i<=n;i++){
if(dp[cnt]<s2[i]) dp[++cnt]=s2[i];
else{
//因为是排列所以low up无所谓
int p=lower_bound(dp+1,dp+1+cnt,s2[i])-dp;
dp[p]=s2[i];
}
}
cout<<cnt<<endl;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++){
cin>>s1[i];
m[s1[i]]=i;
}
for(int i=1;i<=n;i++){
cin>>s2[i];
s2[i]=m[s2[i]];
}
LIS(s2);
}
未完。。。