LIS问题---HDU1025 Constructing Roads In JGShining's Kingdom

发现这个说的比较通俗:

假设存在一个序列d[1..9] = 2 1 5 3 6 4 8 9 7,可以看出来它的LIS长度为5。
下面一步一步试着找出它。
我们定义一个序列B,然后令 i = 1 to 9 逐个考察这个序列。
此外,我们用一个变量Len来记录现在最长算到多少了
首先,把d[1]有序地放到B里,令B[1] = 2,就是说当只有1一个数字2的时候,长度为1的LIS的最小末尾是2。这时Len=1
然后,把d[2]有序地放到B里,令B[1] = 1,就是说长度为1的LIS的最小末尾是1,d[1]=2已经没用了,很容易理解吧。这时Len=1
接着,d[3] = 5,d[3]>B[1],所以令B[1+1]=B[2]=d[3]=5,就是说长度为2的LIS的最小末尾是5,很容易理解吧。这时候B[1..2] = 1, 5,Len=2
再来,d[4] = 3,它正好加在1,5之间,放在1的位置显然不合适,因为1小于3,长度为1的LIS最小末尾应该是1,这样很容易推知,长度为2的LIS最小末尾是3,于是可以把5淘汰掉,这时候B[1..2] = 1, 3,Len = 2
继续,d[5] = 6,它在3后面,因为B[2] = 3, 而6在3后面,于是很容易可以推知B[3] = 6, 这时B[1..3] = 1, 3, 6,还是很容易理解吧? Len = 3 了噢。
第6个, d[6] = 4,你看它在3和6之间,于是我们就可以把6替换掉,得到B[3] = 4。B[1..3] = 1, 3, 4, Len继续等于3
第7个, d[7] = 8,它很大,比4大,嗯。于是B[4] = 8。Len变成4了
第8个, d[8] = 9,得到B[5] = 9,嗯。Len继续增大,到5了。
最后一个, d[9] = 7,它在B[3] = 4和B[4] = 8之间,所以我们知道,最新的B[4] =7,B[1..5] = 1, 3, 4, 7, 9,Len = 5。
于是我们知道了LIS的长度为5。
!!!!! 注意。这个1,3,4,7,9不是LIS,它只是存储的对应长度LIS的最小末尾。有了这个末尾,我们就可以一个一个地插入数据。虽然最后一个d[9] = 7更新进去对于这组数据没有什么意义,但是如果后面再出现两个数字 8 和 9,那么就可以把8更新到d[5], 9更新到d[6],得出LIS的长度为6。
然后应该发现一件事情了:在B中插入数据是有序的,而且是进行替换而不需要挪动——也就是说,我们可以使用二分查找,将每一个数字的插入时间优化到O(logN)~~~~~于是算法的时间复杂度就降低到了O(NlogN)~

  还有一个严格证明的,都很不错:

设  A[t]表示序列中的第t个数,F[t]表示从1到t这一段中以t结尾的最长上升子序列的长度,初始时设F [t] = 0(t = 1, 2,  ..., len(A))。则有动态规划方程:F[t] = max{1, F[j] + 1} (j = 1, 2, ..., t - 1,  且A[j] < A[t])。

现在,我们仔细考虑计算F[t]时的情况。假设有两个元素A[x]和A[y],满足 (1)x < y < t             (2)A[x] < A[y] < A[t]             (3)F[x] = F[y]

此时,选择F[x]和选择F[y]都可以得到同样的F[t]值,那么,在最长上升子序列的这个位置中,应该选择A[x]还是应该选择A[y]呢?

很明显,选择A[x]比选择A[y]要好。因为由于条件(2),在A[x+1] ...  A[t-1]这一段中,如果存在A[z],A[x] < A[z] < a[y],则与选择A[y]相比,将会得到更长的上升子序列。             再根据条件(3),我们会得到一个启示:根据F[]的值进行分类。对于F[]的每一个取值k,我们只需要保留满足F[t] =  k的所有A[t]中的最小值。设D[k]记录这个值,即D[k] = min{A[t]} (F[t] = k)。
            注意到D[]的两个特点: (1) D[k]的值是在整个计算过程中是单调不下降的。 (2) D[]的值是有序的,即D[1] < D[2] < D[3] < ... < D[n]。
            利  用D[],我们可以得到另外一种计算最长上升子序列长度的方法。设当前已经求出的最长上升子序列长度为len。先判断A[t]与D[len]。若A  [t] > D[len],则将A[t]接在D[len]后将得到一个更长的上升子序列,len = len + 1, D[len] = A  [t];否则,在D[1]..D[len]中,找到最大的j,满足D[j] < A[t]。令k = j + 1,则有A [t] <=  D[k],将A[t]接在D[j]后将得到一个更长的上升子序列,更新D[k] = A[t]。最后,len即为所要求的最长上 升子序列的长度。
            在  上述算法中,若使用朴素的顺序查找在D[1]..D[len]查找,由于共有O(n)个元素需要计算,每次计算时的复杂度是O(n),则整个算法的  时间复杂度为O(n^2),与原来的算法相比没有任何进步。但是由于D[]的特点(2),我们在D[]中查找时,可以使用二分查找高效地完成,则整个算法 的时间复杂度下降为O(nlogn),有了非常显著的提高。需要注意的是,D[]在算法结束后记录的并不是一个符合题意的最长上升子序列!

 1 #include <iostream>
 2 using namespace std;
 3 int find(int *a,int len,int n)//若返回值为x,则a[x]>=n>a[x-1]
 4 {
 5     int left=0,right=len,mid=(left+right)/2;
 6     while(left<=right)
 7     {
 8         if(n>a[mid]) left=mid+1;
 9         else if(n<a[mid]) right=mid-1;
10         else return mid;
11         mid=(left+right)/2;
12     }
13     return left;
14 }
15
16 void fill(int *a,int n)
17 {
18     for(int i=0;i<=n;i++)
19         a[i]=1000;
20 }
21
22 int main(void)
23 {
24     int max,i,j,n,a[100],b[100],c[100];
25     while(cin>>n)
26     {
27         fill(c,n+1);
28         for(i=0;i<n;i++)
29             cin>>a[i];
30         c[0]=-1;//     …………………………………1
31         c[1]=a[0];//         …………………………2
32         b[0]=1;//      …………………………………3
33         for(i=1;i<n;i++)//           ………………4
34         {
35             j=find(c,n+1,a[i]);//  …………………5
36             c[j]=a[i];// ………………………………6
37             b[i]=j;//……………………………………7
38         }
39         for(max=i=0;i<n;i++)// ………………………8
40             if(b[i]>max)
41                 max=b[i];
42        cout<<max<<endl;
43     }
44     return 0;
45 }&nbsp;

对于这段程序,我们可以用算法导论上的loop  invariants来帮助理解.

loop invariant :

          1、每次循环结束后c都是单调递增的。(这一性质决定了可以用二分查找)

2、每次循环后,c[i]总是保存长度为i的递增子序列的最末的元素,若长度为i的递增子序列有多个,刚保存末尾元素最小的那个.(这一性质决定是第3条性质成立的前提)                      3、每次循环完后,b[i]总是保存以a[i]结尾的最长递增子序列。

     initialization:

           1、进入循环之前,c[0]=-1,c[1]=a[0],c的其他元素均为1000,c是单调递增的;

2、进入循环之前,c[1]=a[0],保存了长度为1时的递增序列的最末的元素,且此时长度为1 的递增了序列只有一个,c[1]也是最小的;                                                                                   3、 进入循环之前,b[0]=1,此时以a[0]结尾的最长递增子序列的长度为1.

maintenance:

           1、若在第n次循环之前c是单调递增的,则第n次循环时,c的值只在第6行发生变化,而由 c进入循环前单调递增及find函数的性质可知(见find的注释), 此时c[j+1]>c[j]>=a[i]>c[j-1],所以把c[j]的值更新为a[i]后,c[j+1]>c[j]> c[j-1]的性质仍然成 立,即c仍然是单调递增的;

                    2、循环中,c的值只在第6行发生变化,由c[j]>=a[i]可知,c[j]更新为a[i]后,c[j]的值只会变  小不会变大,因为进入循环前c[j]的值是最小的,则循环中把c[j]更新为更小的a[i],当 然此时c[j]的值仍是最小的;

         3、循环中,b[i]的值在第7行发生了变化,因为有loop  invariant的性质2,find函数返回值 为j有:c[j-1]<a[i]<=c[j],这说明c[j-1]是小于a[i]的,且以c[j-1]结尾的递增子序列有最大的 长度,即为j-1,把a[i]接在c[j-1]后可得到以a[i]结尾的最长递增子序列,长度为(j-1)+1=j;

termination:

          循环完后,i=n-1,b[0],b[1],...,b[n-1]的值均已求出,即以a[0],a[1],...,a[n-1]结尾的最长递 增子序列的长度均已求出,再通过第8行的循环,即求出了整个数组的最长递增子序列。

    仔细分析上面的代码可以发现,每次循环结束后,假设已经求出c[1],c[2],c[3],...,c[len]的值,则此时最长递增子序列的长度为 len,因此可以把上面的代码更加简化,即可以不需要数组b来辅助存储,第8行的循环也可以省略。

 1 #include <iostream>
 2 #include <cstdio>
 3
 4 using namespace std;
 5
 6 int poor[500008],Update[500008];
 7 int main()
 8 {
 9     int a,b,n,len,high,low,mid,cal=1;
10     while(scanf("%d",&n)!=EOF)
11     {
12         for(int i=1;i<=n;i++) {scanf("%d%d",&a,&b);poor[a]=b;}
13         len=1;Update[1]=poor[1];
14         for(int i=1;i<=n;i++)
15         {
16             low=1;high=len;
17             while(low<=high)
18             {
19                 mid=(low+high)/2;
20                 if(Update[mid]>=poor[i]) high=mid-1;
21                 else low=mid+1;
22             }
23             Update[low]=poor[i];
24             if(low>len) len++;
25         }
26         printf("Case %d:\n",cal++);
27         if(len>1) printf("My king, at most %d roads can be built.\n\n",len);
28         else printf("My king, at most 1 road can be built.\n\n");
29     }
30     return 0;
31 }

LIS问题---HDU1025 Constructing Roads In JGShining's Kingdom,布布扣,bubuko.com

LIS问题---HDU1025 Constructing Roads In JGShining's Kingdom

时间: 2024-08-11 01:35:04

LIS问题---HDU1025 Constructing Roads In JGShining's Kingdom的相关文章

hdu1025 Constructing Roads In JGShining&#39;s Kingdom (nlogn的LIS)

题目链接 第一次写nlogn复杂度的LIS,纪念一下. 题目意思是说.有两条平行线,两条平行线都有n个城市,都是从左到右标记为1--n,一条线上是富有城市,一个是贫穷城市.输入n,接下来有n行,p,r表示穷城市p和富有城市r 之间可以建一条路(p的顺序是1--n,一个贫穷城市只对应一个富有城市(弱爆的语文描述能力T_T)),公路不能交叉. 问最多可以建多少条公路. 在别处看到的对nlogn解法的解释吧算是: 时间复杂度:(NlogN): 除了算法一的定义之外,增加一个数组b,b[i]用以表示长度

hdu1025 Constructing Roads In JGShining&#39;s Kingdom

转载请注明出处:http://blog.csdn.net/u012860063 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1025 Problem Description JGShining's kingdom consists of 2n(n is no more than 500,000) small cities which are located in two parallel lines. Half of these cities a

hdu-1025 Constructing Roads In JGShining&#39;s Kingdom(二分查找)

题目链接: Constructing Roads In JGShining's Kingdom Time Limit: 2000/1000 MS (Java/Others)     Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 21045    Accepted Submission(s): 5950 Problem Description JGShining's kingdom consists of 2n(n is

HDU-1025 Constructing Roads In JGShining&#39;s Kingdom O(nlogn)的最长上升子序列

模板题,唯一问题是当长度为1是,road是单数,不然road是复数roads. #include <iostream> #include <cstdio> #include <cmath> #include <cstring> #include <queue> #include <vector> #include <cstdlib> #include <algorithm> using namespace st

hdu1025 Constructing Roads In JGShining&amp;#39;s Kingdom (nlogn的LIS)

pid=1025">题目链接 第一次写nlogn复杂度的LIS,纪念一下. 题目意思是说.有两条平行线,两条平行线都有n个城市,都是从左到右标记为1--n,一条线上是富有城市,一个是贫穷城市.输入n,接下来有n行,p,r表示穷城市p和富有城市r 之间能够建一条路(p的顺序是1--n,一个贫穷城市仅仅相应一个富有城市(弱爆的语文描写叙述能力T_T)).公路不能交叉. 问最多能够建多少条公路. 在别处看到的对nlogn解法的解释吧算是: 时间复杂度:(NlogN): 除了算法一的定义之外.添加

hdu1025 Constructing Roads In JGShining&amp;#39;s Kingdom(二分+dp)

转载请注明出处:http://blog.csdn.net/u012860063 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1025 Problem Description JGShining's kingdom consists of 2n(n is no more than 500,000) small cities which are located in two parallel lines. Half of these cities a

HDU1025——LIS——Constructing Roads In JGShining&#39;s Kingdom

Problem Description JGShining's kingdom consists of 2n(n is no more than 500,000) small cities which are located in two parallel lines. Half of these cities are rich in resource (we call them rich cities) while the others are short of resource (we ca

Constructing Roads In JGShining&#39;s Kingdom(HDU 1025 LIS nlogn方法)

Constructing Roads In JGShining's Kingdom Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 21002    Accepted Submission(s): 5935 Problem Description JGShining's kingdom consists of 2n(n is no mor

HDU 1025 Constructing Roads In JGShining&#39;s Kingdom   LIS 简单题 好题 超级坑

Constructing Roads In JGShining's Kingdom Problem Description JGShining's kingdom consists of 2n(n is no more than 500,000) small cities which are located in two parallel lines. Half of these cities are rich in resource (we call them rich cities) whi