题意是要在两条平行线间连点,要在线不交叉的前提下尽可能多的连线,问最多能连多少条线。

现假定题中所给的是 9 组点,分别是:1—3,2—8,3—5,4—9,5—2,6—4,7—6,8—7,9—1,如图示:

 则将所给的每组 p 和 r 存在数组 a[ ] 中:a[ p ] = r;

问题就转化成了在数组 a[ ] 中求最长上升子序列的长度。显然要是直接暴力做的话,每个数字都有选和不选两种可能,那么就是 O( 2 ^ n ) 的复杂度,这是无法接受的。

除暴力外求解最长上升子序列长度的方法有三种(仅本人了解到的):

一、直接用数组去记录选择到该数的最长上升子序列长度,每次从前往后扫一遍,若扫到的数字比所求解位置的数字小,且到该数字的最长上升子序列长度 +1大于已有的到该数字的最长上

升子序列长度,则更新到该数字的最长上升子序列长度,求出这些长度的最大值即为序列的最长上升子序列长度。

以上述 9 组点为例,要求 a[ ] = { 0,3,8,5,9,2,4, 6,7,1 } 的最长上升子序列长( 0 只是占位,与所求数据无关 ),则初始化数组 d[ ] 为 0 :

d[ 1 ] = 1;

d[ 2 ] = 2; ( a[ 2 ] > a[ 1 ] && d[ 2 ] < d[ 1 ] + 1 )

d[ 3 ] = 2; ( a[ 3 ] > a[ 1 ] && d[ 3 ] < d[ 1 ] + 1 )

d[ 4 ] = 3; ( a[ 4 ] > a[ 3 ] && d[ 4 ] < d[ 3 ] + 1 )

......

得到数组 d[ ] 的最大值为 4,则数列的最长上升子序列的长度为 4 。

代码如下:

 #include <bits/stdc++.h>
using namespace std;
int n,p,r,cnt,len,a[],d[];
int main()
{
cnt = ;
while(~scanf("%d",&n))
{
memset(d,,sizeof(d));
for(int i = ; i < n; ++i)
{
scanf("%d%d",&p,&r);
a[p] = r;
}
len = d[] = ;
for(int i = ; i <= n; ++i)
for(int j = ; j < i; j++)
{
if(a[i]>a[j] && d[i]<d[j]+)
d[i] = d[j]+;
else d[i] = ;
if(len < d[i]) len = d[i];
}
printf("Case %d:\n",cnt++);
if(len==) printf("My king, at most 1 road can be built.\n\n");
else printf("My king, at most %d roads can be built.\n\n",len);
}
return ;
}

但是这种 O( n ^ 2 ) 复杂度的方法是会超时的。

二、多开一个数组存放当前已选中数的顺序,用二分的方法依次找到每一个待处理数字的位置再对所选序列进行调整,调整的方法就是将所处理数字按序插入所选序列中,用它替换比它大的下一个数,

若所选序列不存在比它大的数,则将它直接加在所选序列的最后,序列最终的长度就是数列的最大上升子序列长度。主要的思想就是在不损失已有最大升序长度的基础之上将大的数换小,

增大后续出现的数字比当前所选序列最大值更大的可能性。

继续以上述 9 组点为例,这一次的数组 a[ ] 仍然存放数列,数组 d[ ] 存放所选数列:

d[ ] = { 3 }, len = 1; (第一个数 3 直接加入所选序列)

d[ ] = { 3,8 }, len = 2; (第二个数 8 ,所选序列中没有比它大的数,直接加在最后)

d[ ] = { 3,5 }, len = 2; (第三个数 5 ,位于所选序列的 3 与 8 之间,以 5 替换 8 )

d[ ] = { 3,5,9 }, len = 3; (第四个数 9 ,所选序列中没有比它大的数,直接加在最后)

d[ ] = { 2,5,9 }, len = 3; (第五个数 2 ,位于所选序列的 3 之前,以 2 替换 3 )

d[ ] = { 2,4,9 }, len = 3; (第六个数 4 ,位于所选序列的 2 与 5 之间,以 4 替换 5 )

d[ ] = { 2,4,6 }, len = 3; (第七个数 6 ,位于所选序列的 4 与 9 之间,以 6 替换 9 )

d[ ] = { 2,4,6,7 }, len = 4; (第八个数 7 ,所选序列中没有比它大的数,直接加在最后)

d[ ] = {1,4,6,7 }, len = 4; (第九个数 1 ,位于所选序列的 2 之前,以 1 替换 2)

则数列的最长上升子序列长度为 4 。

代码如下:

 #include <bits/stdc++.h>
using namespace std;
int n,len,cnt,a[],d[];
int getp(int x,int l,int r)
{
int mid;
while(l <= r)
{
mid = (l+r)>>;
if(d[mid] == x) return mid;
if(d[mid] > x) r = mid-;
else l = mid+;
}
return l;
}
int main()
{
int p,r;
cnt = ;
while(~scanf("%d",&n))
{
for(int i = ; i < n; ++i)
{
scanf("%d%d",&p,&r);
a[p] = r;
}
d[] = a[];
len = ;
for(int i = ; i <= n; ++i)
{
if(a[i]>d[len-]) d[len++] = a[i];
else d[getp(a[i],,len)] = a[i];
}
printf("Case %d:\n",cnt++);
if(len==) printf("My king, at most 1 road can be built.\n\n");
else printf("My king, at most %d roads can be built.\n\n",len);
}
return ;
}

这个做法仅 O( nlogn ) 的复杂度,主要是在找所处理数在所选序列的位置时使用了二分的方法优化得到的。

三、树状数组优化。等今晚学会了明天再写......

致谢: https://www.cnblogs.com/GodA/p/5180560.html

    https://blog.csdn.net/George__Yu/article/details/75896330

    https://blog.csdn.net/u011721440/article/details/21107113

    非常感谢这些博客作者的贡献。

HDU 1025(最长上升子序列)的更多相关文章

  1. hdu 1950 最长上升子序列(lis) nlogn算法【dp】

    这个博客说的已经很好了.http://blog.csdn.net/shuangde800/article/details/7474903 简单记录一下自己学的: 问题就是求一个数列最长上升子序列的长度 ...

  2. HDU 5773 最长上升子序列

    题意 给出一个序列 问它的最长严格上升子序列多长 这个序列中的0可以被替代为任何数 n的范围给出了1e5 所以平常的O(n*n)lis不能用了 在kuangbin的模板里有O(nlogn)的模板 套上 ...

  3. hdu 1950 最长上升子序列

    //Accepted 3540 KB 62 ms //dp 最长上升子序列 #include <cstdio> #include <cstring> #include < ...

  4. HDU 5748 最长上升子序列的长度nlogn(固定尾部)

    Bellovin Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total ...

  5. HDU 1159 最长公共子序列(n*m)

    Common Subsequence Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Other ...

  6. HDU 1159 最长公共子序列

    Common Subsequence Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Other ...

  7. hdu 5773 最长递增子序列 (nlogn)+贪心

    The All-purpose Zero Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Oth ...

  8. HDU - 1503 最长公共子序列记录路径

    题意:先给两个水果的名字然后得出一个最短的序列包含这两个词. 思路:我一开始的思路是先求出最长公共子序列,然后做一些处理将其他的部分输出来:两种水果的字符串和最长公共子序列的字符串这三个字符串做对比, ...

  9. HDU - 1160 最长上升子序列以及记录路径

    题意:第一列,给出老鼠的重量,第二列,给出老鼠的速度,要证明老鼠的重量越大,速度越小,给出最多老鼠的数量,并说明第几只. 思路:先将老鼠按照重量从大到小排序,然后速度是从小到大,求最长上升子序列,学习 ...

随机推荐

  1. wstngfw IKEv2服务器配置示例

    wstngfw IKEv2服务器配置示例 移动客户端的服务器配置有几个组件: 为***创建一个证书结构 配置IPsec移动客户端设置 为客户端连接创建阶段1和阶段2 添加IPsec防火墙规则 创建** ...

  2. Android应用开发资源

    Android应用设计和开发人员现在可以参考由Android用户体验(UX)团队官方发布的Android设计指南.该指南提供了开发者应该遵循的基本原则,并列出了很多细节指导,涉及设备与显示.主题.触控 ...

  3. 【转】分享两个基于MDK IDE的调试输出技巧

    我们在STM32开发调试过程中,常常需要做些直观的输出,如果手头没有相关的设备或仪器,我们可以使用 IDE自带的工具.这里分享两个基于MDK  IDE的调试输出技巧. 一.使用其自带的逻辑分析仪查看波 ...

  4. 【dfs】LETTERS

    1212:LETTERS [题目描述] 给出一个roe×colroe×col的大写字母矩阵,一开始的位置为左上角,你可以向上下左右四个方向移动,并且不能移向曾经经过的字母.问最多可以经过几个字母. [ ...

  5. HR_Jumping on the Clouds

    1.没有考虑i+2越界的问题 2.没有考虑结尾三个零导致 -5 3.没有考虑len(c)<2 导致 -5 #!/bin/python3 import math import os import ...

  6. LOJ#6284. 数列分块入门 8

    分块的时候开一个数组标记这个区间是不是都是一样颜色的部分,如果是的话,我后面的查询,更新部分就可以直接整块操作,对于不是不全部都一样颜色的块在具体进到快里面去暴力. 在更新的时候对边上的两个不完整的块 ...

  7. [SDOI2015]序列统计(多项式快速幂)

    题目描述 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S.小C用这个生成器生成了许多这样的数列.但是小C有一个问 ...

  8. hdu 1527 (威佐夫博弈)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1527 Problem Description 有两堆石子,数量任意,可以不同.游戏开始由两个人轮流取石 ...

  9. mui 对话框 点击按钮不关闭对话框的办法

    目前版本的 mui.js 点击对话框的按钮只能关闭对话框 做如下修改 点击按钮后return false 即可

  10. canvas简易画板

    代码展示: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF ...