日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

codevs 1576 最长严格上升子序列

發(fā)布時間:2023/12/13 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 codevs 1576 最长严格上升子序列 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
題目鏈接:http://codevs.cn/problem/1576/ 題目描述?Description

給一個數(shù)組a1, a2 ... an,找到最長的上升降子序列ab1<ab2< .. <abk,其中b1<b2<..bk。

輸出長度即可。

輸入描述?Input Description

第一行,一個整數(shù)N。

第二行 ,N個整數(shù)(N?<?=?5000)

輸出描述?Output Description

輸出K的極大值,即最長不下降子序列的長度

樣例輸入?Sample Input

5

9?3?6?2?7

樣例輸出?Sample Output

3

數(shù)據(jù)范圍及提示?Data Size & Hint

【樣例解釋】

最長不下降子序列為3,6,7

解題思路

參考:北大郭煒老師

1.找子問題:“求以ak( k=1, 2, 3…N)為終點的最長上升子序列的長度”
一個上升子序列中最右邊的那個數(shù),稱為該子序列的“終點”。
雖然這個子問題和原問題形式上并不完全一樣,但是只要這N個子問題都解決了,那么這N個子問題的解中,最大的那個就是整個問題的解。

2. 確定狀態(tài)
子問題只和一個變量-- 數(shù)字的位置相關(guān)。因此序列中數(shù)的位置k 就是“狀態(tài)”,而狀態(tài) k 對應(yīng)的“值”,就是以a[k]做為“終點”的最長上升子序列的長度。狀態(tài)一共有N個。

3. 找出狀態(tài)轉(zhuǎn)移方程

maxLen [k]表示以a[k]做為“終點”的最長上升子序列的長度那么:
初始狀態(tài): maxLen [1] = 1
maxLen[k]= max { maxLen [i]: 1<=i < k 且 a[i ]< a[k]且 k≠1 } + 1
若找不到這樣的i,則maxLen[k] = 1

maxLen[k]的值,就是在a[k]左邊,“終點”數(shù)值小于a[k]?,且長度最大的那個上升子序列的長度再加1。因為a[k]左邊任何“終點”小于a[k]的子序列,加上a[k]后就能形成一個更長的上升子序列 。

1 #include <stdio.h> 2 #define maxN 5005 3 int n,a[maxN],maxLen[maxN];//maxLen[k]表示以a[k]做為“終點”的最長上升子序列的長度 4 int main(int argc, char *argv[]) 5 { 6 int i,j; 7 scanf("%d",&n); 8 for(i=0;i<n;i++) { scanf("%d",&a[i]); maxLen[i]=1; } 9 10 for(i=1;i<n;i++)//枚舉所有子序列的終點 11 { 12 for(j=0;j<i;j++)//枚舉以a[i]做終點的子序列中a[i]的前綴元素 13 { 14 if(a[j]<a[i])//嘗試用a[j]做a[i]的直接前綴形成新的子序列 15 { 16 maxLen[i]=(maxLen[j]+1>maxLen[i]?maxLen[j]+1:maxLen[i]); 17 } 18 } 19 } 20 printf("%d\n",maxLen[n-1]); 21 return 0; 22 }

上面的代碼寫錯了,抱歉。更正如下:

1 #include <stdio.h> 2 #define maxN 5005 3 int main(int argc, char *argv[]) 4 { 5 int i,j,t; 6 int n,a[maxN],maxLen[maxN];//maxLen[k]表示以a[k]做為“終點”的最長上升子序列的長度 7 int max; 8 9 scanf("%d",&n); 10 for(i=0;i<n;i++) { scanf("%d",&a[i]); maxLen[i]=1; } 11 for(i=1;i<n;i++)//枚舉所有子序列的終點 12 { 13 for(j=0;j<i;j++)//枚舉以a[i]做終點的子序列中a[i]的前綴元素 14 { 15 if(a[j]<a[i])//嘗試用a[j]做a[i]的直接前綴形成新的子序列 16 { 17 maxLen[i]=(maxLen[j]+1>maxLen[i]?maxLen[j]+1:maxLen[i]); 18 } 19 } 20 } 21 max=maxLen[0]; 22 for(i=1;i<n;i++) 23 if(maxLen[i]>max) max=maxLen[i]; 24 printf("%d\n",max); 25 return 0; 26 }

?

思考題 : 如何改進程序,使之能夠輸出最長上升子序列 ?

思路:新增pre[ ],其中pre[k]=x表示在a[ ]序列構(gòu)成的若干個上升子序列中,a[k]的前驅(qū)是a[x]。一開始pre[ ]全部初始化為-1表示一開始所有元素的前驅(qū)都是自己本身。在循環(huán)求解maxLen[i]的同時,更新pre[i]。最后在掃描出maxLen[ ]最大值為maxLen[i]以后,從pre[i]往前推即可。假如要順序輸出該最長上升子序列,可以把逆推pre[ ]的過程保存再輸出。

參考代碼:

1 #include<stdio.h> 2 #include<string.h> 3 #define maxN 5005 4 int main(int argc, char *argv[]) 5 { 6 int i,j,t; 7 int n,a[maxN],maxLen[maxN];//maxLen[k]表示以a[k]做為“終點”的最長上升子序列的長度 8 int max; 9 int pre[maxN]; 10 int c[maxN],maxIndex; 11 12 memset(pre,-1,sizeof(pre)); 13 14 scanf("%d",&n); 15 for(i=0;i<n;i++) { scanf("%d",&a[i]); maxLen[i]=1; } 16 17 for(i=1;i<n;i++)//枚舉所有子序列的終點 18 { 19 for(j=0;j<i;j++)//枚舉以a[i]做終點的子序列中a[i]的前綴元素 20 { 21 if(a[j]<a[i])//嘗試用a[j]做a[i]的直接前綴形成新的子序列 22 { 23 if(maxLen[j]+1>maxLen[i]) 24 { 25 maxLen[i]=maxLen[j]+1; 26 pre[i]=j; 27 } 28 } 29 } 30 } 31 max=maxLen[0]; 32 for(i=1;i<n;i++) 33 if(maxLen[i]>max) { max=maxLen[i]; maxIndex=i; } 34 printf("%d\n",max); 35 36 j=0; 37 c[j++]=a[maxIndex]; 38 while(pre[maxIndex]!=-1) 39 { 40 maxIndex=pre[maxIndex]; 41 c[j++]=a[maxIndex]; 42 } 43 for(i=j-1;i>=0;i--) 44 { 45 printf("%d ",c[i]); 46 } 47 printf("\n"); 48 return 0; 49 } View Code

?

?

輸出最長上升子序列的另一種思路:

1 #include <stdio.h> 2 int n,size,a[1005][5],s,ans,next; 3 int main(int argc, char *argv[]) 4 { 5 scanf("%d",&n); 6 for(int i=0;i<n;i++) 7 { 8 int t; 9 scanf("%d",&t); 10 a[i][0]=t;a[i][1]=1;a[i][2]=0; 11 //a[i][1]表示以a[i][0]開頭的最長上升子序列的長度。 12 //a[i][2]表示在以a[i][0]開頭的最長上升子序列中a[i][0]的下一個數(shù)在原序列中的下標。 13 } 14 15 for(int i=n-2;i>=0;i--) 16 { 17 size=next=0; 18 for(int j=i+1;j<n;j++) 19 if(a[j][0]>a[i][0]&&a[j][1]>size) {size=a[j][1];next=j;} 20 if(size>0) {a[i][1]=size+1;a[i][2]=next;} 21 } 22 23 ans=0; 24 for(int i=0;i<n;i++) 25 if(a[i][1]>a[ans][1]) ans=i; 26 27 printf("%d\n",a[ans][1]); 28 29 /*for(int i=0;i<n;i++) 30 printf("%d %d %d %d\n",a[i][0],a[i][1],a[i][2],i);*/ 31 32 int i=ans; 33 while(a[i][2]>0) 34 { 35 printf("%d ",a[i][0]); 36 i=a[i][2]; 37 } 38 printf("%d\n",a[i][0]); 39 return 0; 40 } View Code

?

測試OJ地址:

http://noi.openjudge.cn/ch0206/1759/

http://bailian.openjudge.cn/practice/2757/

?

轉(zhuǎn)載于:https://www.cnblogs.com/huashanqingzhu/p/7326739.html

總結(jié)

以上是生活随笔為你收集整理的codevs 1576 最长严格上升子序列的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。