動態規划---子序列的個數


子序列的個數

題目詳情: 子序列的定義:對於一個序列a=a[1],a[2],......a[n],則非空序列a'=a[p1],a[p2]......a[pm]為a的一個子序列,其中1<=p1<p2<.....<pm<=n。

例如:4,14,2,3和14,1,2,3都為4,13,14,1,2,3的子序列。 對於給出序列a,有些子序列可能是相同的,這里只算做1個,要求輸出a的不同子序列的數量。

輸入: 長度為n的數組1<=n<=100,數組元素0<=a[i]<=110

輸出:子序列 的個數對1000000007取余數的結果(由於答案比較大,輸出Mod 1000000007的結果即可)。

函數頭部: C/C++:   int run(cons int *a,int n); java   public static int run(int [] a); 

  這道題目也是困擾了我很久,一直找不到比較好的方法,這道題目應該是可以用動態規划做出來的,因此我也特地去學習動態規划的思想,並找了一些練習題做,可是這個狀態轉移方程着實難住我了,本來數學基礎一般般,這就更加大了難度。雖然我最終解決了這道題目,可是那是建立在大神指點的情況下做出來的,我在這里只是把題解寫出來,順便裨補闕漏,看看自己的理解是否正確,其實,想和做是兩回事,這里也請大家給與指正。

題解:

  假設子序列的前k個數的子序列個數為d(k),那么前k - 1個子序列的個數就為d(k - 1)個子序列,從k - 1k的變化是怎樣的呢?

  1、假設數組a[N]k個數為a[k],如果a[k] 與前面的k - 1個數都不相同,那么就有 : d(k) = d(k - 1) + 【d(k - 1) + 1】 =  2d(k - 1) + 1,為什么呢?可以這樣想,對於前k- 1項的子序列個數為d(k - 1),那前k個數,無非就是在前k - 1項的基礎上多加了一個數a[k](a[k]與前k - 1個數任意一個都相等),那就在原來的組合上加上a[k],就有d(k - 1)個,還有一個a[k]自己構成一個子序列,所以還要加1;

  2、假設a[k] 與前面的k - 1個數其中一個相等,那依舊加上前k - 1個子序列個數 d(k - 1),但是由於前面有與a[k]相等的數,所以要減掉重復的部分,如何找到重復的部分呢,假設離k最近的一個與a[k]相等的數為第t個a[t] = a[k],即序列(a[1], a[2], ……,a[t],……,a[k - 1],a[k]),a[t] = a[k];我們已經知道序列(a[1], a[2], ……,a[t])的序列個數為d(t),那么d(t - 1)就是重復的部分,這里需要自己做好思考,也是算法的關鍵部分,這里我要解釋的地方是,為什么只需要找到離k最近的t使得a[t] = a[k]?給出的解釋是:我們是從1 - n對數組進行遍歷的,計算d(i)的i就是從1到n依次計算的,那么第一次遇到a[k] = a[t]的情況滿足條件:有且僅有一個t使得a[t] = a[k],比如序列(1, 2, 3, 2, 4, 2),分別計算d(1),d(2),d(3),d(4),d(5),d(6);我們在計算d(4)的時候發現a[4] = a[2](假設下標從1開始),所以d(4) = 2*d(3) - d(2 -1) = 2d(3) - d(1);當計算d(6)的時候也有a[6] = a[4] = a[2],但是由於我們前面已經把a[2]重復的部分減掉了,所以不需要再減,d(6) = 2 * d(5) - d(4 - 1) = 2d(5) - d(3).

  過程繁瑣,我總結一下結論:

  狀態轉移方程為:

    d(k) = 2 * d(k - 1) + 1;   a[k] != a[i],i = 1,2,3……k - 1;

     d(k) = 2 * d(k - 1) - d(t - 1);   從k往前搜索,存在離k最近的t,使得a[t] = a[k].

  狀態轉移方程分析出來的話,剩下的基本就是小菜一碟了,代碼如下:

 

/*
    以下是題目詳情: 子序列的定義:對於一個序列a=a[1],a[2],......a[n],
    則非空序列a'=a[p1],a[p2]......a[pm]為a的一個子序列,其中1<=p1<p2<.....<pm<=n。 
    例如:4,14,2,3和14,1,2,3都為4,13,14,1,2,3的子序列。 對於給出序列a,有些子序列可能是相同的,
    這里只算做1個,要求輸出a的不同子序列的數量。 輸入: 長度為n的數組1<=n<=100,數組元素0<=a[i]<=110
    輸出:子序列 的個數對1000000007取余數的結果(由於答案比較大,輸出Mod 1000000007的結果即可)。
    函數頭部: C/C++:   int run(cons int *a,int n); java   public static int run(int [] a);
*/
#include <stdio.h>
#include <stdlib.h>
#define M 1000000007
int run(const int *a,int n)
{
    long long SubArray[120] = {0};
    int LastIndex[120] = {0};
    int iter = 0;
    for(iter = 1; iter <= n; iter++)
    {
        switch(LastIndex[a[iter - 1]])
        {
        case 0:
            {
                SubArray[iter] = (2 * SubArray[iter - 1] + 1) % M;
                break;
            }
        default:
            {
                SubArray[iter] = (2 * SubArray[iter - 1] - SubArray[LastIndex[a[iter - 1]] - 1] + M) % M;
                break;
            }
        }
        LastIndex[a[iter - 1]] = iter;
    }
    return SubArray[n] % M;
}
int main(void)
{
    //int a[5] = {1, 2, 2, 3 ,3};
    int a[4] = {1, 2, 3 , 2};
    printf("the result is %d\n", run(a, 4));
    return 0;
}
View Code

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM