LDU 軟件工程 算法分析與設計(三)- 遞歸與分治

問題 A: 集合劃分問題

題目描述

包含n個元素的集合,可以劃分爲若干個非空子集。例如,當n=3時,集合{1,2,3} 可以構造如下五個劃分: 

{1,2,3}                 {1},{2},{3} 

{1,2},{3}                    {1,3},{2}                    {1},{2,3} 

請設計程序,計算包含n個元素的集合可以構造多少個不同的劃分。 

輸入

一個整數n,(1<=n<=25)

輸出

一個整數

樣例輸入

1

樣例輸出

1

思想:①貝爾數,集合劃分是貝爾數的一個問題。②DP,dp[i][j]代表是將i個數分到j個集合內

dp[i][j]=dp[i-1][j]*j+dp[i-1][j-1]  意思是當前有i-1個數和j個集合,你將第i個數放到j個集合中的一個有個j中選擇,或者是當前這個數單獨放一個集合。

long long B[30];
long long T[30];
const int N = 26;
void Bell()  
{  
    B[0] = 1;  
    B[1] = 1;  
    T[0] = 1;  
    for(int i=2;i<N;i++)  
    {  
        T[i-1] = B[i-1];  
        for(int j=i-2;j>=0;j--)  
            T[j] = (T[j]+T[j+1]);  
        B[i] = T[0];  
    }  
}   
int main()
{
    Bell();
    int temp;
    scanf("%d",&temp); 
    printf("%lld\n",B[temp]);
    return 0;
} 

問題 B: 樓梯臺階問題

題目描述

有一樓梯共M級,剛開始時你在第一級,若每次只能跨上一級或二級,要走上第M級,共有多少種走法?

輸入

第一行一個整數T,表示測試組數。 
接下來T行每行一個整數M(1<=M<=40) 

輸出

輸出T行 
每行一個整數 

樣例輸入

1
4

樣例輸出

3

思想:第 i 階臺階由i-1跨一步或者i-2跨2步過來,所以dp[i]=dp[i-1]+dp[i-2].預處理下dp[1] dp[2] dp[3]即可

對於第一階臺階是0還是1,假如考慮在1不動算一種情況就是1 否則就就是0

問題 C: 小蜜蜂

題目描述

有一隻經過訓練的蜜蜂只能爬向右側相鄰的蜂房,不能反向爬行。請編程計算蜜蜂從蜂房a爬到蜂房b的可能路線數。其中,蜂房的結構如下所示。

 

輸入

一個整數T表示測試組數。 
接下來T行,每行兩個整數a和b(0<a<b<50) 

輸出

T行 
每行一個整數 

樣例輸入

2
1 2
3 6

樣例輸出

1
3

思想:考慮下對於任何點來說它只能通過相鄰的2個過來,所以就可以考慮枚舉下每個點的貢獻,每個點只能對於+1 +2有貢獻.

ll dp[60];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        memset(dp,0,sizeof(dp));
        int a,b;
        scanf("%d%d",&a,&b);
        dp[a+1]=1;
        dp[a+2]=2;
        for(int i=a+3;i<=b;i++)
        {
            dp[i]=dp[i-1]+dp[i-2];
        }
        printf("%d\n",dp[b]);
    }
    return 0;
}

問題 D: ​切面條問題

題目描述

一根高筋拉麪,中間切一刀,可以得到2根麪條。如果先對摺1次,中間切一刀,可以得到3根麪條。如果連續對摺2次,中間切一刀,可以得到5根麪條。那麼,連續對摺n次,中間切一刀,會得到多少麪條呢?

輸入

一個整數n(1<=n<=1000000)

輸出

一個整數,注意,由於答案非常大,要求你輸出答案模除1000000007之後的結果

樣例輸入

3

樣例輸出

9

思想:單獨考慮的每一個話,穩的T,可以將每個點可以推出來一個很好推的式子(毛線) 對於每個n來說 他就等於2^n+1.

無論是線性跑n還是快速冪都OK

問題 E: 整數劃分問題

題目描述

計算將一個給定的正整數劃分爲一系列正整數的和的方案數,稱爲整數的劃分問題,例如,當給定正整數爲6時,可以有如下劃分: 

6=6; 

6=5+1; 

6=4+2=4+1+1; 

6=3+3=3+2+1=3+1+1+1; 

6=2+2+2=2+2+1+1=2+1+1+1+1; 

6=1+1+1+1+1+1+1。 

如果用f(n)代表正整數n的劃分數,則f(6)=11 

現在,給你數字n,要求你計算f(n) 

輸入

一個整數n

輸出

輸出f(n)。注意,由於答案非常大,你的答案需要輸出模除1000000007之後的結果

樣例輸入

6

樣例輸出

11

思想:母函數算法的模板題,如果看到此博客可以學一下母函數,會在其中註釋代碼,仍有不懂可以提問。

const long long mod = 1e9+7;
const int maxn= 1e5+5;
long long c1[maxn];
long long c2[maxn];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<=n;i++)//c1是儲存每個的結果 c2是每次計算的結果
    {
        c1[i]=1;   //初始化對於每一項的係數都是1 
        c2[i]=0;
    }
    for(int i=2;i<=n;i++)//枚舉項數 總共有n個式子
    {
        for(int j=0;j<=n;j++)//枚舉前邊的到的所有項數
        {
            for(int k=0;k+j<=n;k+=i)//對於第i個式子的每個式子的係數 所以是每次加i
                c2[k+j]=(c1[j] + c2[k+j])%mod;  //k+j是乘完之後的次方
        }
        for(int k=0;k<=n;k++)//進行替換
        {
            c1[k]=c2[k];
            c2[k]=0;
        }
    }
    printf("%lld\n",c1[n]%mod);
    return 0;
} 

問題 F: 找單詞(母函數)

題目描述

假設有x1個字母A, x2個字母B,..... x26個字母Z,同時假設字母A的價值爲1,字母B的價值爲2,..... 字母Z的價值爲26。那麼,對於給定的字母,可以找到多少價值<=50的單詞呢?單詞的價值就是組成一個單詞的所有字母的價值之和,比如,單詞ACM的價值是1+3+14=18,單詞HDU的價值是8+4+21=33。(組成的單詞與排列順序無關,比如ACM與CMA認爲是同一個單詞)。

輸入

輸入首先是一個整數N,代表測試實例的個數。
然後包括N行數據,每行包括26個<=20的整數x1,x2,.....x26.

輸出

對於每個測試實例,請輸出能找到的總價值<=50的單詞數,每個實例的輸出佔一行。

樣例輸入

2
1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
9 2 6 2 10 2 2 5 6 1 0 2 7 0 2 2 7 5 10 6 10 2 10 6 1 9

樣例輸出

7
379297

思想:Hdu上的母函數的入門題。跟上邊的那個差不多隻是一些東西稍微改了下,思想還是一樣的,套上板子改改就好了。

對於母函數不懂的可以看上題題解,看懂在看此題更好。

const int maxn= 60;
long long c1[maxn];
long long c2[maxn];
int num[maxn];
int main()
{
    int t; 
    scanf("%d",&t);
    while(t--)
    {
        for(int i=1;i<=26;i++)//輸入每種的數量
            scanf("%d",&num[i]);
        memset(c1,0,sizeof(c1));
        memset(c2,0,sizeof(c2)); 
        for(int i=0;i<=num[0];i++)//將第一個的爲1 和 當初的那個n一樣
            c1[i]=1;    
        for(int i=1;i<=26;i++)//枚舉項 就是那個26項
        {
            for(int j=0;j<=50;j++)//因爲要求不超過50 所以最大的權值應該是50
                for(int k=0;k<=num[i]*i &&  k+j<=50;k+=i)//k的值應該小於等於最大的那個 而且 不會超過50
                    c2[k+j]=(c1[j] + c2[k+j]);  
            for(int k=0;k<=50;k++)
            {
                c1[k]=c2[k];
                c2[k]=0;
            }
        }
        long long ans=0;
        for(int i=1;i<=50;i++)//所有的求一個和
            ans+=c1[i];
        printf("%lld\n",ans);
    }
    return 0;
} 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章