acwing提高班-动态规划2
最长上升子序列问题
#include <bits/stdc++.h>
using namespace std;
const int N = 5010;
int dp[N];
typedef pair<int, int> PP;
PP a[N];
int main()
{
int n;
scanf("%d",&n);
for(int i = 0;i<n;i++)scanf("%d %d",&a[i].first,&a[i].second);
sort(a,a+n);
for(int i = 0;i<n;i++){
dp[i] = 1;
for(int j = 0;j<i;j++){
if(a[i].second > a[j].second){
dp[i] = max(dp[i],dp[j]+1);
}
}
}
int res = 1;
for(int i = 0 ;i<n;i++)res = max(res,dp[i]);
printf("%d\n",res);
return 0;
}
先排序,把问题转化为最长上升子序列问题。
状态都是以i结尾的最i上升子序列或者以i结尾的最长上升子序列和
#include <bits/stdc++.h>
using namespace std;
const int N = 55;
int w[N];
int up[N],down[N];
int ans;
int n;
void dfs(int now,int nu,int nd){
if(nu + nd >= ans)return;
if(now == n){
ans = nu + nd;
return;
}
// 先把now加入到上升子序列中
int k = 0;
while (k<nu && up[k] > w[now])k++;
int t = up[k];
up[k] = w[now];
if(k<nu)dfs(now+1,nu,nd);
else{
dfs(now+1,nu+1,nd);
}
up[k] = t;
// 把now加入到下降子序列中
k = 0;
while (k<nd && down[k] < w[now])k++;
t = down[k];
down[k] = w[now];
if(k<nd)dfs(now+1,nu,nd);
else{
dfs(now+1,nu,nd+1);
}
down[k] = t;
}
int main()
{
while (cin>>n,n){
for(int i = 0;i<n;i++)cin>>w[i];
ans = n;
dfs(0,0,0);
cout<<ans<<endl;
}
return 0;
}
思路是LIS 加上 dfs,dfs求最小值的方法是使用全局最小化或迭代加深,两者均可。不使用bfs是因为bfs一般会有状态爆炸,占用过多内存,且不易于剪枝。
#include <bits/stdc++.h>
using namespace std;
const int N = 3010;
int n;
int a[N],b[N];
int dp[N][N];
int main()
{
scanf("%d",&n);
for(int i = 1;i<=n;i++)scanf("%d",&a[i]);
for(int i = 1;i<=n;i++)scanf("%d",&b[i]);
for(int i = 1;i<=n;i++){
for(int j = 1;j<=n;j++){
dp[i][j] = dp[i-1][j];
if(a[i] == b[j]){
dp[i][j] = max(dp[i][j],1);
for(int k = 1;k<j;k++){
if(b[k] < b[j]){
dp[i][j] = max(dp[i][j],dp[i-1][k]+1);
}
}
}
}
}
int res = 0;
for(int i = 1;i<=n;i++)res = max(res,dp[n][i]);
printf("%d\n",res);
return 0;
}
思路是将最长上升子序列和最长公共子序列结合起来,dp[i][j] 表示的是以b[j]结尾的和a[:i]的最长公共上升子序列长度最大值
优化是在代码层面做等价变形
#include <bits/stdc++.h>
using namespace std;
const int N = 3010;
int n;
int a[N],b[N];
int dp[N][N];
int main()
{
scanf("%d",&n);
for(int i = 1;i<=n;i++)scanf("%d",&a[i]);
for(int i = 1;i<=n;i++)scanf("%d",&b[i]);
for(int i = 1;i<=n;i++){
int maxv = 1;
for(int j = 1;j<=n;j++){
dp[i][j] = dp[i-1][j];
if(a[i] == b[j]) dp[i][j] = max(maxv,dp[i][j]);
if(b[j] < a[i]) maxv = max(dp[i][j]+1,maxv);
}
}
int res = 0;
for(int i = 1;i<=n;i++)res = max(res,dp[n][i]);
printf("%d\n",res);
return 0;
}
记录1-j 中 满足 b[k]<a[i] 的 dp[i][k] 的最大值