CodeVS2185 最长公共上升子序列

来源:互联网 发布:gbk转utf8 java 编辑:程序博客网 时间:2024/06/10 17:02

http://codevs.cn/problem/2185/

题解

  令f[i][j]表示不强制选择a[i]且强制选择b[j]的答案。

  之所以这么设定,经过了一番考虑。如果你让f[i][j]表示a[i]和b[j]都不强制选,那么你无法确定答案所对应的结果中a[i]或者b[j]是否已经匹配,转移似乎就比较比较难想;如果让f[i][j]表示两者都强制选,那么二者不匹配时你就把答案设为负无穷,如果匹配,你要枚举前面哪两个匹配,这样时间复杂度太高。于是我们就让一个不强制选择,一个强制选择。就出来了状态转移方程:

  当a[i]==b[j]时,f[i][j]=f[i-1][k],其中k小于j且满足b[k]<b[j]。当a[i]!=b[j]时,f[i][j]=f[i][j-1]。

  那么框架出来了,枚举i、枚举j,当a[i]==b[j]时还要枚举k。这样的话时间复杂度为O(N^3)

  怎么优化?头疼了半天,后来看了一篇题解就懂了。

  我们暂且先不考虑i这一维,i定住不动,也就是a[i]不变,要计算所有的j∈[1,N]的f[i][j]。当a[i]==b[j]时,就要枚举f[i-1][k],需满足b[k]<b[j],而由于a[i]的不变,所有需要转移的b[j]的值也就是相等的,也就是说你之前枚举的所有k,在后来的转移中一定也会用到,所以我们每加入一个新的枚举对象(即当出现一个a[i]==b[j])时,就打擂台统计max,转移的时候,直接将max+1赋给f[i][j]就行了。

  思想:这里的优化中,打擂台求最小其实没啥,最有用的一个思想就是把大的维度定住不动,只考虑低维度,这样就能把高维问题转化为低维问题,大大降低了思维复杂度。这貌似是所有DP优化中共用的思想吧?

  ps:这道题如果规定匹配的区间,能否用单调队列做呢?

代码

#include <cstdio>#include <algorithm>#define maxn 3010using namespace std;int f[maxn][maxn], a[maxn], b[maxn], N;int main(){int i, j, maxx, ans=0;scanf("%d",&N);for(i=1;i<=N;i++)scanf("%d",a+i);for(i=1;i<=N;i++)scanf("%d",b+i);for(i=1;i<=N;i++){maxx=0;for(j=1;j<=N;j++){if(a[i]==b[j])f[i][j]=maxx+1;else f[i][j]=f[i-1][j];if(a[i]>b[j])maxx=max(maxx,f[i-1][j]);ans=max(ans,f[i][j]);}}printf("%d\n",ans);return 0;}


0 0
原创粉丝点击