【JZOJ4762】【NOIP2016提高A组模拟9.7】千帆渡

来源:互联网 发布:mysql 添加子分区 编辑:程序博客网 时间:2024/06/05 05:09

题目描述

这里写图片描述

输入

这里写图片描述

输出

这里写图片描述

样例输入

5
1 4 2 5 1
4
1 1 2 4

样例输出

2
1 4

数据范围

这里写图片描述

解法

设f[i][j]表示 i个蓝色帆船中,选择了 j个红色帆船作为结尾的最大答案。
那么:
f[i][j]=max(f[i1][k]+1)(k<j,a[k]<b[j],a[i]=b[j])
f[i][j]=f[i1][j](a[i]!=b[j])
考虑优化,从k入手:
维护h[i][j]表示在f[i][j] (a[i]=b[j])时最大的k使得满足转移条件。
h[i][j]可以从h[i-1][k]中转移过来。

代码

#include<iostream>#include<stdio.h>#include<string.h>#include<math.h>#include<algorithm>using namespace std;const char* fin="aP2.in";const char* fout="aP2beta.out";const int inf=0x7fffffff;const int maxn=5007;int read(){    char ch=getchar();    int x=0;    while (ch<'0' || ch>'9') ch=getchar();    while (ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();    return x;}int n,m,i,j,k,l,o,ans=0,ans1,ans2;int cans[maxn];int a[maxn],b[maxn];int f[maxn][maxn],g[maxn][maxn],h[maxn][maxn];int main(){    scanf("%d",&n);    for (i=1;i<=n;i++) scanf("%d",&a[i]);    scanf("%d",&m);    for (i=1;i<=m;i++) scanf("%d",&b[i]);    for (i=1;i<=n;i++)        for (j=1;j<=n;j++){            if (a[i]==b[j]){                k=h[i][j];                if (k<j && b[k]<b[j] && f[i][j]<f[i-1][k]+1){                    f[i][j]=f[i-1][k]+1;                    g[i][j]=k;                    if (ans<f[i][j]){                        ans1=i;                        ans2=j;                        ans=f[i][j];                    }                }            }else f[i][j]=f[i-1][j],g[i][j]=j;            if (b[j]<a[i+1]) {                if (f[i][h[i+1][j-1]]<f[i][j]) h[i+1][j]=j;                else h[i+1][j]=h[i+1][j-1];            }            else h[i+1][j]=h[i+1][j-1];        }    printf("%d\n",ans);    while (ans1){        if (a[ans1]==b[ans2]){            cans[++cans[0]]=a[ans1];        }        j=ans1;        k=ans2;        ans1=j-1;        ans2=g[j][k];    }    for (i=ans;i;i--) printf("%d ",cans[i]);    return 0;}

启发

当初把f[i][j]设成了选了第i个蓝色帆船和第j个红色帆船的最大答案。
然后使得动态规划优化困难。
不过也运用了去除冗余状态的方法,把这个O(n^3)的动态规划优化到了80分。
联系以前的启发;


事实上动态规划的第一维都可以设成前i个什么。
这样方便优化。

0 0