分享到plurk 分享到twitter 分享到facebook

版本 bf24e60463eb0424c1ae4daaece0000aa091c318

acm/course/LIS

Week 6: LIS(Longest Increasing Subsequence )

  • increasing: 嚴格遞增
  • subsequence: sub + sequence 。 sub 有著「次要」的意思,而 sequence 是指數學之中的「數列」、「序列」。
  • LIS: 指一個 sequence 當中,它擁有最長的長度、且嚴格遞增的那些 subsequence (不一定只有一個)。
    • ex.1 3 5 2 9 的 LIS 是 1 3 5 9 這個 subsequence 。

##Dynamic Programming 概念

Recurence:
length(n) =  max  { length(i) + 1 : if s[i] < s[n] }    ( 0≤i≤n-1)
n:第0項到第n項的LIS。
length(n):第0項到第n項的LIS長度。
s[n]:第n項數值。

計算LIS長度

int s[5];       // sequence
int length[5];  // 第 x 格的值為 s[0...x] 的 LIS 長度
 
void LIS()
{
    // 初始化。每一個數字本身就是長度為一的 LIS。
    for (int i=0; i<5; i++) length[i] = 1;
 
    for (int i=0; i<5; i++)
        // 找出 s[i] 後面能接上哪些數字,
        // 若是可以接,長度就增加。
        for (int j=i+1; j<5; j++)
            if (s[i] < s[j])
                length[j] = max(length[j], length[i] + 1);
 
    // length[] 之中最大的值即為 LIS 的長度。
    int n = 0;
    for (int i=0; i<5; i++)
        n = max(n, length[i]);
    cout << "LIS的長度是" << n;
}

找出LIS

/*用一個陣列紀錄一個數字是接在哪個數字後面*/

int s[5];
int length[5];
int prev[5]; // prev[x] 紀錄 s[x] 是接在哪個數字後面
 
void LIS()
{
    for (int i=0; i<5; i++) length[i] = 1;
    // -1 代表 s[i] 是開頭數字,沒有接在其他數字後面。
    for (int i=0; i<5; i++) prev[i] = -1;
 
    for (int i=0; i<5; i++)
        for (int j=i+1; j<5; j++)
            if (s[i] < s[j])
                if (length[i] + 1 > length[j])
                {
                    length[j] = length[i] + 1;
                    // s[j] 接在 s[i] 後面
                    prev[j] = i;
                }
 
    int n = 0, pos = 0;
    for (int i=0; i<5; i++)
        if (length[i] > n)
        {
            n = length[i];
            pos = i;
        }
 
    trace(pos); // 印出一個LIS
}
 
// 遞迴版本
void trace(int i)
{
    if (prev[i] != -1) trace(prev[i]);
    cout << seq[i] << ' ';
}
 
// 迴圈版本,但是順序會顛倒。
void trace(int i)
{
    for (; prev[i] != -1; i = prev[i])
        cout << seq[i] << ' ';
}