题目链接HDUhttp://acm.hdu.edu.cn/showproblem.php?pid=1423(不需要输出路径) ZOJhttp://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1432(输出路径)
【题意】求两个序列的最长公共子序列,经典算法;(注意:HDU输出格式和ZOJ类似,需要每两个case之间加一行空行)
【分析】
(1)n^3模板
dp[i][j]表示序列a前i个和序列b前j个字符,并且以b[j]作为LCIS序列的末尾的LCIS的长度
转移方程
1) dp[i][j] = dp[i-1][j] (a[i] != b[j])
//a[i] != a[j]时不满足两个序列公共,所以不会增加长度,又因为dp[][]是以b[j]作为结尾的新序列下的长度,所以直接等于dp[i-1][j]的值
2) dp[i][j] = {dp[i-1][k] | 1 <= k < j} (a[i] == b[j])
//只有a[i] == b[j]时才会有可能使得长度增加,然后对b[](其实主要是对a[k] == b[m](k < j)的序列)进行LIS就可以了
(2)n^2模版
看看LIS中的条件:b[k]<b[j](1<=k<=j-1)&&a[i]==b[j] 可以推出a[i]>b[j](这里的j相当于下个循环的枚举的k),用一个maxn变量来记录dp[i-1][1~j]的最大值,可以省去LIS中的每次都重复查找最大值,这样可以把复杂度降低一维。
(3)n^2滚动数组模板
因为dp[i][..]使用的都是上一次的dp[i-1][...],当然可以像01背包那样用滚动数组优化,第二个循环不是倒序,因为这里有点像完全背包那样,本次循环更新之后,还可以在本次基础上再更新;使用滚动数组就没办法输出路径了,因为路径需要以前的状态才能输出。
【AC代码(n^3)ZOJ】720ms
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 510
struct NODE{//记录路径
int x, y;
}f[MAXN][MAXN];
int a[MAXN], b[MAXN], dp[MAXN][MAXN], ex, ey;
void pt(int i, int j)
{
if (!i || !j)
return;
int x = f[i][j].x, y = f[i][j].y;
pt(x, y);
if (dp[x][y]+1 == dp[i][j])
{
printf ("%d", b[j]);
if (i != ex && j != ey)
printf (" ");
}
}
int main ()
{
#ifdef SHY
freopen("e:\\1.txt", "r", stdin);
#endif
int t;
scanf ("%d%*c", &t);
while(t--)
{
int na, nb, ans = 0;
ex = 0, ey = 0;
scanf ("%d%*c", &na);
for (int i = 1; i <= na; i++)
scanf ("%d%*c", &a[i]);
scanf ("%d%*c", &nb);
for (int i = 1; i <= nb; i++)
scanf ("%d%*c", &b[i]);
memset(dp,0,sizeof(dp));
memset(f,0,sizeof(f));
for (int i = 1; i <= na; i++)
{
for (int j = 1; j <= nb; j++)
{
if (a[i] == b[j])
{
dp[i][j] = 1;
for (int k = 1; k < j; k++)
{
if (b[k] < b[j] && dp[i][j] < dp[i-1][k]+1)
dp[i][j]= dp[i-1][k]+1, f[i][j].x = i-1,f[i][j].y = k;
}
if (ans < dp[i][j])
ans = dp[i][j], ex = i, ey = j;
}
else
dp[i][j] = dp[i-1][j], f[i][j].x = i-1, f[i][j].y = j;
}
}
printf ("%d\n", ans);
pt(ex,ey);
printf ("\n");
if (t)
printf ("\n");
}
return 0;
}
【n^2算法ZOJ】30ms//利用了下标1开始,所以下标为0的一直不会被改变始终是初始化中的0
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 503
struct NODE{
int x, y;
}f[MAXN][MAXN];
int a[MAXN], b[MAXN], dp[MAXN][MAXN], ex, ey;
void pt(int i, int j)
{
if (!i || !j)
return;
int x = f[i][j].x, y = f[i][j].y;
pt(x, y);
if (dp[x][y]+1 == dp[i][j])
printf ("%d ", b[j]);
}
int main ()
{
#ifdef SHY
freopen("e:\\1.txt", "r", stdin);
#endif
int t;
scanf ("%d%*c", &t);
while(t--)
{
int na, nb, ans = 0;
ex = 0, ey = 0;
scanf ("%d%*c", &na);
for (int i = 1; i <= na; i++)
scanf ("%d%*c", &a[i]);
scanf ("%d%*c", &nb);
for (int i = 1; i <= nb; i++)
scanf ("%d%*c", &b[i]);
for (int i = 1; i <= na; i++)
{
int maxn = 0, x = 0, y = 0;
for (int j = 1; j <= nb; j++)
{
if (a[i] == b[j])
{
dp[i][j] = maxn+1;
f[i][j].x = x, f[i][j].y = y;
if (ans < dp[i][j])
ans = dp[i][j], ex = i, ey = j;
}
else
{
dp[i][j] = dp[i-1][j], f[i][j].x = i-1, f[i][j].y = j;
if (a[i] > b[j] && maxn < dp[i-1][j])
maxn = dp[i-1][j], x = i, y = j;
}
}
}
printf ("%d\n", ans);
pt(ex,ey);
printf ("\n");
if (t)
printf ("\n");
}
return 0;
}
【n^2(滚动数组)HDU】0ms
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 503
int a[MAXN], b[MAXN], dp[MAXN];
int main ()
{
#ifdef SHY
freopen("e:\\1.txt", "r", stdin);
#endif
int t;
scanf ("%d%*c", &t);
while(t--)
{
int na, nb, ans = 0;
scanf ("%d%*c", &na);
for (int i = 1; i <= na; i++)
scanf ("%d%*c", &a[i]);
scanf ("%d%*c", &nb);
for (int i = 1; i <= nb; i++)
scanf ("%d%*c", &b[i]);
memset(dp,0,sizeof(dp));
for (int i = 1; i <= na; i++)
{
int maxn = 0;
for (int j = 1; j <= nb; j++)
{
if (a[i] == b[j])
{
dp[j] = maxn+1;
if (ans < dp[j])
ans = dp[j];
}
else if (a[i] > b[j] && maxn < dp[j])
maxn = dp[j];
}
}
printf ("%d\n", ans);
if (t)
printf ("\n");
}
return 0;
}