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

题目描述

【JZOJ4762】【NOIP2016提高A组模拟9.7】千帆渡_第1张图片

输入

这里写图片描述

输出

这里写图片描述

样例输入

5
1 4 2 5 1
4
1 1 2 4

样例输出

2
1 4

数据范围

【JZOJ4762】【NOIP2016提高A组模拟9.7】千帆渡_第2张图片

解法

设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
#include
#include
#include
#include
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 (k1][k]+1){
                    f[i][j]=f[i-1][k]+1;
                    g[i][j]=k;
                    if (anselse f[i][j]=f[i-1][j],g[i][j]=j;
            if (b[j]1]) {
                if (f[i][h[i+1][j-1]]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个什么。
这样方便优化。

转载于:https://www.cnblogs.com/hiweibolu/p/6714903.html

你可能感兴趣的:(【JZOJ4762】【NOIP2016提高A组模拟9.7】千帆渡)