题解 CF10D 【LCIS】
LCIS问题
Solution of LCIS Problem - By Nash
本文进行了路径保存方面的讨论。
upd.2021.8.10 初版编辑时各方面经验不足,现重新排版,小幅度重新组织语言
由于与初版编辑时间间隔相当长,如与现有题解重复请管理直接撤下。
【知识基础】
-
LCS (最长公共子序列,
O(n^2) ) -
LIS (最长上升子序列,
O(n^2) ,本题未用到O(n\log n) 做法)
【题意简述】
求
【状态设计】
设
仿照 LCS 问题,我们很容易想到直接表示出状态的设法:
但这样我们在转移时无法获得
于是,综合 LIS 的思想,我们限定 该 LCIS 以
这样,状态转移就更容易实现了。
【状态转移方程】
我们选择枚举当前状态
- 若
A[i] \not= B[j]
因为要求
于是可得
- 若
A[i] = B[j]
在
在这些公共上升子序列中,选取一个最长的就是解
所以
总的方程就是
最后的答案是
时间复杂度
【路径输出】
路径输出有两种方法,分别如下
方法1
假设我们直接开一个动态长度的数组(可以用vector) 储存 LCIS ,每次转移时进行更新。
具体来说,建立二维数组
其中
每次转移时将
再在后面的
最后找到 LCIS 最长的结束位置 ansj ,将
这种方法中,由于多重 if 语句的限制,实际运行速度很快,
甚至可以通过 N=5000 的多组极端数据,但其时间复杂度上界仍为
【核心代码1】
for (int i=1; i<=N; i++)
{
for (int j=1; j<=M; j++)
{
if (A[i] == B[j])
{
f[i][j] = 1;
lcis[j][1] = B[j];
for (int k=1; k<j; k++)
if (B[k]<B[j])
{
if (f[i-1][k]+1 > f[i][j])
{
f[i][j] = f[i-1][k]+1;
for (int p=1; p<=f[i-1][k]; p++)
lcis[j][p] = lcis[k][p];
lcis[j][f[i][j]] = B[j];
}
}
}
else f[i][j] = f[i-1][j];
}
}
for(int p=1;p<=f[N][ansj];p++)
printf("%d ",lcis[ansj][p]);
puts("");
方法2
设
每次随着
输出的递归函数应有两个参数, Output(i,j) 沿着 Output(i-1,path[i][j]) ,并在
若
这种方法的时间复杂度为
核心代码2
for (int i=1; i<=N; i++)
{
for (int j=1; j<=M; j++)
{
if (A[i] == B[j])
{
for (int k=1; k<j; k++)
if (B[k]<B[j])
if (f[i-1][k]+1 > f[i][j])
{
f[i][j] = f[i-1][k]+1;
path[i][j] = k;
}
}
else
{
f[i][j] = f[i-1][j];
path[i][j] = j;
}
}
}
void Output(int i,int j)
{
if(i==0) return;
Output(i-1,path[i][j]);
if (path[i][j] != j)
printf("%d ",B[j]);
return;
}
if(f[N][ansj])
{
Output(N,ansj);
puts("");
}
【优化】
注意到在
也就是决策集合
每次
该决策合法的条件是
虽然随着
但是,只有
因为枚举
当
即 if (B[j]<A[i] && f[i-1][j]>f[i-1][k]) k=j;
这样就可以不用在
优化后两种方法的时间复杂度都降了一维,时间复杂度为
【总结】
-
状态转移时,应注意决策集合的范围变化,设计出相应的维护方式,如用单一变量记录最优值或用线段树、单调队列等数据结构优化.
-
记录路径的数组应与答案数组的维度相同,且记录的信息应为抽象的转移路径,而不是"上一个数"等较直观的信息,否则容易被覆盖出错.
最后献上AC代码两份
方法1
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAXN=5005;
int N, M, A[MAXN], B[MAXN];
int f[MAXN][MAXN], ansj=0, lcis[MAXN][MAXN];
void LCIS();
void Input();
int main()
{
Input();
LCIS();
printf("%d\n",f[N][ansj]);
for (int p=1; p<=f[N][ansj]; p++)
printf("%d ",lcis[ansj][p]);
puts("");
return 0;
}
void LCIS()
{
memset (f, 0, sizeof(f));
memset (lcis, 0, sizeof(lcis));
for (int i=1; i<=N; i++)
{
for (int j=1,k=0; j<=M; j++)
{
if (A[i] == B[j])
{
f[i][j] = f[i-1][k]+1;
for (int p=1; p<=f[i-1][k]; p++)
lcis[j][p] = lcis[k][p];
lcis[j][f[i][j]] = A[i];
}
else f[i][j] = f[i-1][j];
if (B[j]<A[i] && f[i][j]>f[i][k])
k = j;
}
}
for (int i=1; i<=M; i++)
if (f[N][i] > f[N][ansj])
ansj = i;
return;
}
void Input()
{
scanf("%d",&N);
for(int i=1; i<=N; i++)
scanf("%d",&A[i]);
scanf("%d",&M);
for(int i=1; i<=M; i++)
scanf("%d",&B[i]);
return;
}
方法2
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAXN=5005;
int N, M, A[MAXN], B[MAXN];
int f[MAXN][MAXN], path[MAXN][MAXN], ansj=0;
void LCIS();
void Input();
void Output(int,int);
int main()
{
Input();
LCIS();
printf("%d\n", f[N][ansj]);
if (f[N][ansj])
{
Output(N,ansj);
puts("");
}
return 0;
}
void LCIS()
{
memset (f, 0, sizeof(f));
memset (path, 0, sizeof(path));
for (int i=1; i<=N; i++)
{
for (int j=1,k=0; j<=M; j++)
{
if (A[i] == B[j])
{
f[i][j] = f[i-1][k]+1;
path[i][j] = k;
}
else
{
f[i][j] = f[i-1][j];
path[i][j] = j;
}
if(B[j]<A[i] && f[i][j]>f[i][k])
k = j;
}
}
for(int i=1; i<=M; i++)
if(f[N][i] > f[N][ansj])
ansj = i;
return;
}
void Output(int i,int j)
{
if(i==0) return;
Output(i-1,path[i][j]);
if (path[i][j] != j)
printf("%d ",B[j]);
return;
}
void Input()
{
scanf("%d",&N);
for (int i=1; i<=N; i++)
scanf("%d",&A[i]);
scanf("%d",&M);
for (int i=1; i<=M; i++)
scanf("%d",&B[i]);
return;
}
附:我在尝试只用一维数组存路径时被Hack掉的数据
Input:
100
9060 2520 8100 4950 4950 6020 2360 2230 7100 7100 2360 8100 2520 2520 9060 2520 9065 2520 8100 9065 9065 2520 9065 4950 2520 9490 6020 2520 8100 2520 3400 1934 7100 9060 8100 2520 3400 1934 3400 1934 9490 9184 2230 2520 2360 9065 7100 2230 2520 2360 9065 2520 7100 1934 8100 2520 6020 5650 2520 5650 5650 9490 5650 1934 4950 4360 9060 6020 9184 2520 4950 9060 8100 1934 9184 2360 4950 6020 4360 7100 2360 8100 1934 9060 2520 3400 3400 4360 1934 8100 1934 2520 4360 5650 4360 6020 1934 2230 9065 2520
100
4950 8100 7100 9065 9060 7100 7100 6020 4950 2360 9184 2230 5650 4360 4950 5650 6020 7100 3400 7100 4360 9065 2230 2520 9065 3400 2360 3400 9490 4360 2230 4360 2520 8100 8100 2360 2230 8100 6020 5650 2230 4950 3400 3400 2360 4360 9065 9490 1934 2230 9060 5650 7100 2520 3400 2360 7100 2230 5650 8100 9184 9065 1934 7100 8100 1934 8100 2230 2230 7100 4950 5650 3400 5650 2360 2230 9065 9490 1934 9060 1934 5650 9184 9184 8100 6020 5650 1934 2230 4360 4360 2230 8100 4950 1934 2360 9060 2360 2230 4950
Output:
9
2230 2360 2520 3400 4360 4950 6020 8100 9060