这篇讲的挺好的:https://blog.csdn.net/lxt_Lucia/article/details/81206439
最长上升子序列的话,我们用dp [ i ] 表示截至a [ i ] 为止(以a[ i ]结尾)的最长上升子序列的长度,这也是为什么我们初始化dp[ i ]为1,因为开始就是已经包含了a[ i ]的。
如果在a [ i ]之前有比a [ i ]小的数a [ j ] ,就说明a [ i ]可以加到a [ j ]处的最长子序列后,因为可能 j 从1-> i-1 有多个小于a[ i ]的数a[ j ],所以取其中长度的较大者,即dp [ i ] = max(dp [ i ] ,dp [ j ] +1)
可能很多人都有一个疑问,就是这样处理dp,并不一定是最长上升子序列,对的,就是这样的,但是这并不影响我们要的答案,我们只是需要最长的长度,dp的更新虽然使最长子序列里的值发生了变化,但是并不影响长度。
代码:
const int maxN=4e4+5;
const int maxC=1e6+5;
int T,n,dp[maxN],a[maxN],ans;
void init()
{
fill(dp,dp+n+1,1);
ans=0;
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
init();
for(int i=1;i<=n;i++)
{
for(int j=1;j<i;j++)
{
if(a[i]>a[j])
{
dp[i]=max(dp[i],dp[j]+1);
}
}
}
for(int i=1;i<=n;i++)
{
ans=max(ans,dp[i]);
}
printf("%d\n",ans);
}
return 0;
}
HDU 1950 Bridging signals//模板题
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1950
题意:这个题意很简单,就是最长上升子序列的模板。
但是它的题目吧,超级难读,我也是醉了,严重怀疑自己是不是学过英语。n多不认识的词和短语,一大段一大段的不懂,不明白,然后各种有道翻译,终于读到最后的时候,我的天呐,题意竟然就这么简单,写的那么复杂,什么不交叉,架桥之类的QAQ,我哭了。
题解:
这里我们用low[ i ]来表示 i 长度的上升子序列目前为止最小的末尾值。如果我们能保证末尾值一直是最小的,就可以得到尽可能长的上升子序列。
我们跑一个for循环,如果a[ i ] > low[ ans ] (ans是当前最长子序列的长度,也是low数组的长度),那么我们可以直接将a[ i ]加到low[++ans]数组中;否则,我们就找到low数组中第一个大于等于a[ i ]的数,并用a[ i ]替换掉它,这里的查找就可以直接用lower_bound二分查找(注意是得到的地址-首地址才是下标值)。
这里的话我们low数组中并不是最长公共子序列,也是有了些元素的替换,但是不改变长度,我们做这样的替换是为了后面能够得到更长的序列,可以自行体会一下。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define P(x) x>0?x:0
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef vector<int>:: iterator VITer;
const int maxN=4e4+5;
const int maxC=1e6+5;
int T,n,a[maxN];
int low[maxN],ans;
void init()
{
fill(low,low+n+1,0);
ans=0;
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
init();
for(int i=1;i<=n;i++)
{
if(a[i]>low[ans])
low[++ans]=a[i];
else
low[lower_bound(low,low+ans+1,a[i])-low]=a[i];//lower_bound返回的是low中第一个大于等于a[i]的地址,再减去首地址就是该元素的下标,替换成a[i]
}
printf("%d\n",ans);
}
return 0;
}
最长上升子序列(单调不降)
Power OJ 2870
思路:我们要保证可以存在重复的数字,所以说我们的 low 数组就可以存在重复的数字,并且当我们遇到较小的数字时我们要放在 low 数组中第一个大于它的数字的位置上。(必须是不能替代小于等于的数字,否则答案就会偏小)
样例:(用 lower_bound就会错)
6
1 2 5 2 2 2
wrong answer: 3
:)就是直接2每次都覆盖2,所以长度不会变
#include
#include
#include
#include
using namespace std;
const int maxN=1e5+5;
int ans, a[maxN], low[maxN] , n;
void init()
{
ans = 0;
memset(low, 0 , sizeof(low));
}
int LIS()
{
for(int i = 1; i <= n ; i++)
{
if( a[i] >= low[ans])
low[++ans] = a[i];
else
low[ upper_bound(low, low + ans + 1, a[i]) - low ] = a[i];
}
return ans;
}
int main()
{
while(~scanf("%d", &n))
{
init();
for(int i = 1; i <= n ; i++)
scanf("%d", &a[i]);
printf("%d\n", LIS());
}
return 0;
}
Day Summary:(以下内容可以忽略,日常发牢骚)
今天基本上就只看了这个,最长上升子序列和最长公共子序列,别人前天就已经会了的东西。上午来的时候,难受,不敲题,不看题,发呆,难受,难过,给我妈打电话,她不懂,挂了,回来,十点多吧,又狠着心看了最长公共子序列。到中午吃饭了,然后就又开始发呆,十二点没过多少就吃完了,然后就一直到一点钟,然后就睡了,本来想着一点半起来,然后一直睡到两点钟,又很难受,又发呆,中间还出去骑车骑了一圈,还是不想看题,就……emmm难受呀QAQ,心里难受,非常难受。一直到四点多,终于可以看得下去点儿东西了,就把这个看了,敲了两道题。还是看了人家的博客。我这个人比较情绪化吧,心情不好,心里难受了是完全什么都做不了的,就这个心吧,越难受它就越难受。感觉要病了,抑郁症,自杀离我不远了(当然是开玩笑,我还没活够)但是真的很难受也很难过。至于为什么,我太菜了吧,当然还有别的。本来不想回家的这个暑假,我感觉我快了,回家,遭不住了就直接溜了,不管吧。像我这样的,在哪里都是一样的,做不成事情。
我这几天一直在想,我每天浪费那么多时间难受,发呆,我还在这干啥,还不如回家去耍,但是我又狠不下心走,可能还在可接受范围内吧,这种程度,还没到高三那时候的那样呢,不过也快了我觉得,不说了,为自己祈福,希望可以慢慢好起来。