1. 程式人生 > >RQNOJ 201 奧運大包圍:LIS + 拼鏈成環

RQNOJ 201 奧運大包圍:LIS + 拼鏈成環

一次 fin include names ont 能夠 code max href

題目鏈接:https://www.rqnoj.cn/problem/201

題意:

  開始時n(n<=1000)個人手拉手圍成一個圈。

  後來這些人中的一些按順序向裏面出圈形成一個新圈。從而使原圈形成一個從高到低,最低與最高連接的圈。

  新圈重復相同的操作,直到沒有人要出圈為止。

  問最少要形成多少個這樣的圈。

題解:

  (1)拼鏈成環:

    對於一個環,可以用兩條由環拆開的鏈拼在一起表示。

    例如:有一個環為"1,2,3,4"(1和4連在一起),則可以表示為"1,2,3,4,1,2,3"。

       每一次從不同位置遍歷環時,只需要枚舉前n個點作為起點,向後遍歷n個即可。

  (2)轉化問題:

    原題可以變成:求最少有幾個相互不重疊的嚴格下降子序列,能夠將最初的環完全覆蓋。

  (3)設計算法:

    首先有一個定理:下降子序列的個數 = 最長非降子序列的長度

    那麽此題跟NOIP攔截導彈的第二問就一模一樣了。

    所以枚舉每個起點,求一下最小的LIS(非降)就好啦。

    因為n<=1000,所以求LIS要用nlogn的方法。總復雜度O(N^2*logn)。

AC Code:

#include <iostream>
#include <stdio.h>
#include <string.h>
#define
MAX_N 2005 #define INF 10000000 using namespace std; int n; int ans; int a[MAX_N]; int d[MAX_N]; void read() { cin>>n; for(int i=0;i<n;i++) { cin>>a[i]; a[n+i]=a[i]; } } int cal_lis(int *a) { int len=1; d[1]=a[0]; for(int i=1;i<n;i++) {
if(a[i]>=d[len]) { d[++len]=a[i]; } else { int lef=1; int rig=len; while(rig-lef>1) { int mid=(lef+rig)/2; if(a[i]>=d[mid]) lef=mid; else rig=mid; } int res; if(a[i]>=d[rig]) res=rig; else if(a[i]>=d[lef]) res=lef; else res=0; d[res+1]=min(d[res+1],a[i]); } } return len; } void solve() { ans=INF; for(int i=0;i<n;i++) { ans=min(ans,cal_lis(a+i)); } } void print() { cout<<ans<<endl; } int main() { read(); solve(); print(); }

RQNOJ 201 奧運大包圍:LIS + 拼鏈成環