题目:http://acm.hdu.edu.cn/showproblem.php?pid=1423
题目大意:给你两个序列,让你求他们的LCIS。
解题思路:假设两个序列分别为num1,num2。
首先,如果做过LCS,应该马上会想到用 d[ i ][ j ] 来表示序列 num1 的前 i 个、序列 num2 的前 j 个的最长的长度,但是转换的时候,因为是上升,并不知道这个 d[ i - 1 ][ j - 1 ] 转换过来的时候那个值是不是比 当前的小,所以我们这样设计状态d [ i ][ j ],就是序列 num1 的前 i 个,num2 的前 j 个且以num2[ j ] 为结尾的LCIS,那么还需要在 for 一遍,比num1[ i ]( 也就是 num2[ j ]) 小的转换过来。
代码是这样的(时间复杂度O(n^3),空间复杂度O(n^2)):
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 555;
int num1[MAXN],num2[MAXN];
int d[MAXN][MAXN];
int main()
{
int cas = 0;
int _;
scanf("%d",&_);
while(_--)
{
if(cas++ > 0) puts("");
int n1,n2;
scanf("%d",&n1);
for(int i = 1;i <= n1;i++) scanf("%d",&num1[i]);
scanf("%d",&n2);
for(int i = 1;i <= n2;i++) scanf("%d",&num2[i]);
memset(d,0,sizeof(d));
for(int i = 1;i <= n1;i++)
{
for(int j = 1;j <= n2;j++)
{
d[i][j] = d[i-1][j];
if(num1[i] == num2[j])
{
d[i][j] = max(d[i][j],1);
for(int k = 1;k < j;k++)
if(num2[j] > num2[k]) d[i][j] = max(d[i][j],d[i-1][k]+1);
}
//printf("i = %d,j = %d,d = %d\n",i,j,d[i][j]);
}
}
int ans = 0;
for(int i = 1;i <= n2;i++) ans = max(ans,d[n1][i]);
printf("%d\n",ans);
}
return 0;
}
有没有觉得就是最里面那个 k 循环那里搞得时间复杂度有点高,是的,其实仔细想想会发现那一层其实可以去掉,但是去掉,那状态转移的时候怎么知道大小关系?看第一层循环,每次都是 num1[ i ] 来更新num2,而且 k 又是 num2 j 走过的部分再走一遍,那么我们可以设一个变量 m 作为之前 j 走过的当前 j 要用到的最大值,这样就不用走 k 循环了。由于都是num1[ i ] 来更新,它更新的肯定是num1[ i ] == num2[ j ] 的 d 数组,也就是我们拿 num1[ i ] 和 num2[ j ] 比较,如果 num1[ i ] > num2[ j ] && d[ i - 1 ][ j ] > m ,那么就更新这个 m,然后如果再相等,就直接用 m 更新就好了,这样时间复杂度就降为 O(n^2)了。但是如果你用上面的第一种方式来定义的,个人觉得就不能这样纸优化了。
代码是这样的(时间复杂度:O(n^2),空间复杂度O(n^2)):
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 555;
int num1[MAXN],num2[MAXN];
int d[MAXN][MAXN];
int main()
{
int cas = 0;
int _;
scanf("%d",&_);
while(_--)
{
if(cas++ > 0) puts("");
int n1,n2;
scanf("%d",&n1);
for(int i = 1;i <= n1;i++) scanf("%d",&num1[i]);
scanf("%d",&n2);
for(int i = 1;i <= n2;i++) scanf("%d",&num2[i]);
memset(d,0,sizeof(d));
for(int i = 1;i <= n1;i++)
{
int m = 0;
for(int j = 1;j <= n2;j++)
{
d[i][j] = d[i-1][j];
if(num1[i] > num2[j] && d[i-1][j] > m) m = d[i-1][j];
if(num1[i] == num2[j]) d[i][j] = m+1;
}
}
int ans = 0;
for(int i = 1;i <= n2;i++) ans = max(ans,d[n1][i]);
printf("%d\n",ans);
}
return 0;
}
时间上已经优化为了 O(n^2)了,其实,空间上还能优化。
如果不相等就是 d[ i ][ j ] = d[ i - 1 ][ j ],相等就是 m+1,而且 m 也只是在更新d[ j ] 之后更新的,也就是此时 d[ j ] 存的还是 d[ i - 1][ j ] ,那么我们就只用设一维d[ j ],如果不相等就不用变,如果相等,就是 m+1。
但是说回来,其实空间优化不用怎么计较,时间过了,空间也差不多能过,要不然,你空间优化,因为它只跟上一层有关,第一维搞成 0、1 变化的不就行了。所以推荐还是使用上面那个,清晰一点。
代码是这样的(时间复杂度O(n^2),空间复杂度O(n)):
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 555;
int num1[MAXN],num2[MAXN];
int d[MAXN];
int main()
{
int cas = 0;
int _;
scanf("%d",&_);
while(_--)
{
if(cas++ > 0) puts("");
int n1,n2;
scanf("%d",&n1);
for(int i = 1;i <= n1;i++) scanf("%d",&num1[i]);
scanf("%d",&n2);
for(int i = 1;i <= n2;i++) scanf("%d",&num2[i]);
memset(d,0,sizeof(d));
for(int i = 1;i <= n1;i++)
{
int m = 0;
for(int j = 1;j <= n2;j++)
{
if(num1[i] > num2[j] && d[j] > m) m = d[j];
if(num1[i] == num2[j]) d[j] = m+1;
}
}
int ans = 0;
for(int i = 1;i <= n2;i++) ans = max(ans,d[i]);
printf("%d\n",ans);
}
return 0;
}