JZOJ 4762. 【NOIP2016提高A组模拟9.7】千帆渡
题目
Description
Input
Output
Sample Input
5
1 4 2 5 1
4
1 1 2 4
Sample Output
2
1 4
Data Constraint
题解
好像挺显然地是一道动态规划题。
怎么设状态?
如果设
f
[
i
]
[
j
]
f[i][j]
f[i][j]为以
a
[
i
]
a[i]
a[i]和
b
[
j
]
b[j]
b[j]作为末尾的最大答案,这样必须保证
a
[
i
]
=
b
[
j
]
a[i]=b[j]
a[i]=b[j],不仅会浪费很大的空间,而且转移起来也是
O
(
n
2
m
2
)
O(n^2m^2)
O(n2m2)的,时间必然超限。
但如果设
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示以
a
[
k
]
(
k
≤
i
)
a[k](k≤i)
a[k](k≤i)和
b
[
j
]
b[j]
b[j]作为末尾的最大大案,这样能有一些较为便捷的转移:
1、
a
[
i
]
=
b
[
j
]
a[i]=b[j]
a[i]=b[j],
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
−
1
]
[
k
]
)
(
k
≤
j
f[i][j]=max(f[i-1][k])(k≤j
f[i][j]=max(f[i−1][k])(k≤j且
b
[
k
]
<
b
[
j
]
)
b[k]<b[j])
b[k]<b[j])
2、
a
[
i
]
≠
b
[
j
]
a[i]≠b[j]
a[i]=b[j],
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
]
f[i][j]=f[i-1][j]
f[i][j]=f[i−1][j]
怎么统计方案?
在存储最大长度的同时记录下由什么位置转移得到,最后从后往前倒推求值即可。
但这样是
O
(
n
m
2
)
O(nm^2)
O(nm2)的,还要优化。
很显然地,第一种情况的
m
a
x
max
max可以用边做边维护。
a
[
i
]
=
b
[
j
]
a[i]=b[j]
a[i]=b[j],
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
−
1
]
[
k
]
)
(
k
≤
j
f[i][j]=max(f[i-1][k])(k≤j
f[i][j]=max(f[i−1][k])(k≤j且
b
[
k
]
<
b
[
j
]
)
b[k]<b[j])
b[k]<b[j]),
其实也就是
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
−
1
]
[
k
]
)
(
k
≤
j
f[i][j]=max(f[i-1][k])(k≤j
f[i][j]=max(f[i−1][k])(k≤j且
b
[
k
]
<
a
[
i
]
)
b[k]<a[i])
b[k]<a[i]),
那么就可以:
当
b
[
j
]
<
a
[
i
]
b[j]<a[i]
b[j]<a[i]时,如果
f
[
i
−
1
]
[
j
]
>
m
a
x
f[i-1][j]>max
f[i−1][j]>max,则
m
a
x
=
f
[
i
−
1
]
[
j
]
max=f[i-1][j]
max=f[i−1][j].
于是这题就轻松地突破了!
代码
#include<cstdio>
#include<cstring>
using namespace std;
int a[5010],b[5010],ans[5010];
int f[5010][5010],g[5010][5010],h[5010][5010];
struct
{
int x,h;
}max[5010];
int main()
{
int n,m,i,j,k;
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<=m;j++)
{
if(a[i]!=b[j])
{
f[i][j]=f[i-1][j];
g[i][j]=g[i-1][j];
h[i][j]=h[i-1][j];
}
else
{
if(max[i-1].x+1>f[i][j])
{
f[i][j]=max[i-1].x+1;
g[i][j]=i-1;
h[i][j]=max[i-1].h;
}
}
if(b[j]<a[i])
{
if(f[i-1][j]>max[i-1].x)
{
max[i-1].x=f[i-1][j];
max[i-1].h=j;
}
}
}
int s=0;
for(i=1;i<=m;i++) if(f[n][i]>f[n][s]) s=i;;
printf("%d\n",f[n][s]);
int x=n,y=s;
s=f[n][s];
while(x!=0&&s>0)
{
ans[s]=b[y];
int x1=x;
x=g[x][y];
y=h[x1][y];
s--;
}
s=0;
while(ans[s+1]!=0) printf("%d ",ans[++s]);
return 0;
}