1. 程式人生 > >遞推DP

遞推DP

HDU2048 神、上帝以及老天爺

HDU 2006'10 ACM contest的頒獎晚會隆重開始了!
為了活躍氣氛,組織者舉行了一個別開生面、獎品豐厚的抽獎活動,這個活動的具體要求是這樣的:

首先,所有參加晚會的人員都將一張寫有自己名字的字條放入抽獎箱中;
然後,待所有字條加入完畢,每人從箱中取一個字條;
最後,如果取得的字條上寫的就是自己的名字,那麼“恭喜你,中獎了!”

大家可以想象一下當時的氣氛之熱烈,畢竟中獎者的獎品是大家夢寐以求的Twins簽名照呀!不過,正如所有試圖設計的喜劇往往以悲劇結尾,這次抽獎活動最後竟然沒有一個人中獎!
我的神、上帝以及老天爺呀,怎麼會這樣呢?
不過,先不要激動,現在問題來了,你能計算一下發生這種情況的概率嗎?
不會算?難道你也想以悲劇結尾?!
 Input
輸入資料的第一行是一個整數C,表示測試例項的個數,然後是C 行資料,每行包含一個整數n(1<n<=20),表示參加抽獎的人數。Output
對於每個測試例項,請輸出發生這種情況的百分比,每個例項的輸出佔一行, 結果保留兩位小數(四捨五入)
Sample Input
1 2
Sample Output
50.00%
 

#include <bits/stdc++.h>
#include <iostream>
#define X 10005
#define inf 0x3f3f3f3f
#define PI 3.141592653589793238462643383
#define IO  ios::sync_with_stdio(false),cin.tie(0), cout.tie(0);
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
const int maxn = (int)1e5 + 10;
typedef long long ll;
ll D[25];
int main()
{
    D[0]=0,D[1]=0,D[2]=1;
    for(int i=3;i<=20;++i)D[i]=(i-1)*(D[i-1]+D[i-2]);
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        cin>>n;
        double sum=exp(lgamma(n+1));
       // cout<<sum<<' '<<exp(lgamma(n+1))<<endl;//<<' '<<D[n]<<endl;
        printf("%.2lf%%\n",D[n]*100.0/sum);
    }
    return 0;
}

HDU2047 阿牛的EOF牛肉串

今年的ACM暑期集訓隊一共有18人,分為6支隊伍。其中有一個叫做EOF的隊伍,由04級的阿牛、XC以及05級的COY組成。在共同的集訓生活中,大家建立了深厚的友誼,阿牛準備做點什麼來紀念這段激情燃燒的歲月,想了一想,阿牛從家裡拿來了一塊上等的牛肉乾,準備在上面刻下一個長度為n的只由"E" "O" "F"三種字元組成的字串(可以只有其中一種或兩種字元,但絕對不能有其他字元),阿牛同時禁止在串中出現O相鄰的情況,他認為,"OO"看起來就像發怒的眼睛,效果不好。
你,NEW ACMer,EOF的崇拜者,能幫阿牛算一下一共有多少種滿足要求的不同的字串嗎?
PS: 阿牛還有一個小祕密,就是準備把這個刻有 EOF的牛肉乾,作為神祕禮物獻給杭電五十週年校慶,可以想象,當校長接過這塊牛肉乾的時候該有多高興!這裡,請允許我代表杭電的ACMer向阿牛表示感謝!
再次感謝!
Input
輸入資料包含多個測試例項,每個測試例項佔一行,由一個整數n組成,(0<n<40)。
Output
對於每個測試例項,請輸出全部的滿足要求的塗法,每個例項的輸出佔一行。
Sample Input
1
2
Sample Output
3
8

#include <bits/stdc++.h>
#include <iostream>
#define X 10005
#define inf 0x3f3f3f3f
#define PI 3.141592653589793238462643383
#define IO  ios::sync_with_stdio(false),cin.tie(0), cout.tie(0);
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
const int maxn = (int)1e5 + 10;
typedef long long ll;
ll f[55];
int main()
{
    int n;
    f[1]=3,f[2]=8;//f[3]=22;
    for(int i=3;i<=40;++i)
        f[i]=(f[i-1]+f[i-2])*2;
    while(cin>>n)
    {
        cout<<f[n]<<endl;
    }
    return 0;
}

HDU 2046 

在2×n的一個長方形方格中,用一個1× 2的骨牌鋪滿方格,輸入n ,輸出鋪放方案的總數.
例如n=3時,為2× 3方格,骨牌的鋪放方案有三種,如下圖:

Input
輸入資料由多行組成,每行包含一個整數n,表示該測試例項的長方形方格的規格是2×n (0<n<=50)
Output
對於每個測試例項,請輸出鋪放方案的總數,每個例項的輸出佔一行。
Sample Input
1 3 2
Sample Output
1 3 2
不和走樓梯每次走1或者2得一樣嘛
 

#include <bits/stdc++.h>
#include <iostream>
#define X 10005
#define inf 0x3f3f3f3f
#define PI 3.141592653589793238462643383
#define IO  ios::sync_with_stdio(false),cin.tie(0), cout.tie(0);
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
const int maxn = (int)1e5 + 10;
typedef long long ll;
ll f[10005];
int main()
{
    int t,n;
    f[1]=1;f[2]=2;
    for(int i=3;i<=50;++i)
        f[i]=f[i-1]+f[i-2];
    while(cin>>n)
    {
        cout<<f[n]<<endl;
    }
    return 0;
}

HDU 2045 不容易系列之(3)—— LELE的RPG難題
人稱“AC女之殺手”的超級偶像LELE最近忽然玩起了深沉,這可急壞了眾多“Cole”(LELE的粉絲,即"可樂"),經過多方打探,某資深Cole終於知道了原因,原來,LELE最近研究起了著名的RPG難題:
有排成一行的n個方格,用紅(Red)、粉(Pink)、綠(Green)三色塗每個格子,每格塗一色,要求任何相鄰的方格不能同色,且首尾兩格也不同色.求全部的滿足要求的塗法.
以上就是著名的RPG難題.
如果你是Cole,我想你一定會想盡辦法幫助LELE解決這個問題的;如果不是,看在眾多漂亮的痛不欲生的Cole女的面子上,你也不會袖手旁觀吧?
Input
輸入資料包含多個測試例項,每個測試例項佔一行,由一個整數N組成,(0<n<=50)。
Output
對於每個測試例項,請輸出全部的滿足要求的塗法,每個例項的輸出佔一行。
Sample Input
1
2
Sample Output
3
6
若第n-1個格子和第一個格子不同,則為f(n-1);
若第n-1個格子和第1個格子相同,則第n-2個格子和第一個格子必然不同,此時為f(n-2)再乘第n-1個格子的顏色數,很顯然第n-1個格子可以是第一個格子(即第n-2個格子)的顏色外的另外兩種,這樣為2*f(n-2);
有個坑就是f(3)=3;
 

#include <bits/stdc++.h>
#include <iostream>
#define X 10005
#define inf 0x3f3f3f3f
#define PI 3.141592653589793238462643383
#define IO  ios::sync_with_stdio(false),cin.tie(0), cout.tie(0);
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
const int maxn = (int)1e5 + 10;
typedef long long ll;
ll f[55];
int main()
{
    int n;
    f[1]=3,f[2]=6;f[3]=6;
    for(int i=4;i<=50;++i)f[i]=f[i-1]+f[i-2]*2;
    while(cin>>n)
    {
        cout<<f[n]<<endl;
    }
    return 0;
}

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

Input
輸入資料的第一行是一個整數N,表示測試例項的個數,然後是N 行資料,每行包含兩個整數a和b(0<a<b<50)。
Output
對於每個測試例項,請輸出蜜蜂從蜂房a爬到蜂房b的可能路線數,每個例項的輸出佔一行。
Sample Input
2 1 2 3 6
Sample Output
1 3
半分鐘看題,一分鐘半分鐘出思路
好,果然wa ,抑鬱了,
,,,,沒開longlong
 

#include <bits/stdc++.h>
#include <iostream>
#define X 10005
#define inf 0x3f3f3f3f
#define PI 3.141592653589793238462643383
#define IO  ios::sync_with_stdio(false),cin.tie(0), cout.tie(0);
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
const int maxn = (int)1e5 + 10;
typedef long long ll;
ll f[55];
int main()
{
    int t,a,b;
    f[1]=1,f[2]=1;
    for(int i=3;i<=50;++i)
        f[i]=f[i-1]+f[i-2];
    cin>>t;
    while(t--)
    {
        cin>>a>>b;
        cout<<f[b-a+1]<<endl;
    }
    return 0;
}

HDU 2041  超級樓梯
有一樓梯共M級,剛開始時你在第一級,若每次只能跨上一級或二級,要走上第M級,共有多少種走法?
Input
輸入資料首先包含一個整數N,表示測試例項的個數,然後是N行資料,每行包含一個整數M(1<=M<=40),表示樓梯的級數。
Output
對於每個測試例項,請輸出不同走法的數量
Sample Input
2
2
3
Sample Output
1
2

#include <bits/stdc++.h>
#include <iostream>
#define X 10005
#define inf 0x3f3f3f3f
#define PI 3.141592653589793238462643383
#define IO  ios::sync_with_stdio(false),cin.tie(0), cout.tie(0);
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
const int maxn = (int)1e5 + 10;
typedef long long ll;
ll f[55];
int main()
{
    f[1]=1,f[2]=1;
    for(int i=3;i<=40;++i)
        f[i]=f[i-1]+f[i-2];
    int t,n;
    cin>>t;
    while(t--)
    {
        cin>>n;
    cout<<f[n]<<endl;
    }
    return 0;
}

HDU2018 母牛的故事

有一頭母牛,它每年年初生一頭小母牛。每頭小母牛從第四個年頭開始,每年年初也生一頭小母牛。請程式設計實現在第n年的時候,共有多少頭母牛?
Input
輸入資料由多個測試例項組成,每個測試例項佔一行,包括一個整數n(0<n<55),n的含義如題目中描述。
n=0表示輸入資料的結束,不做處理。
Output
對於每個測試例項,輸出在第n年的時候母牛的數量。
每個輸出佔一行。
Sample Input
2 4 5 0
Sample Output
2 4 6
寫下前6天得母牛的個數 2 3 4 6 9 13 就會知道f(n)=f(n-1)+f(n-3)

#include <bits/stdc++.h>
#include <iostream>
#define X 10005
#define inf 0x3f3f3f3f
#define PI 3.141592653589793238462643383
#define IO  ios::sync_with_stdio(false),cin.tie(0), cout.tie(0);
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
const int maxn = (int)1e5 + 10;
typedef long long ll;
int f[60];
int main()
{
    int n;
    f[1]=1,f[2]=2,f[3]=3;
    for(int i=4;i<=55;++i)
        f[i]=f[i-1]+f[i-3];
    while(cin>>n,n!=0)
    {
        cout<<f[n]<<endl;
    }
}


CF687C   The Values You Can Mak
  出題
給你一些硬幣,讓你選出一個子集的總價值和為k,然後對於一個選出的子集,除了可以組成k以外,還可以在選出的子集中選出一些其他的價值。問你所有的選出的子集一共可以得到多少種價值。 
看的題解:
這個也可以說是一個01揹包了,裡面也有一些集合的思想在裡面,首先dp方程,dp[i][j]代表著當前數值為i,j能否被構成,如果dp[i][j] = 1,那麼dp[i+m][j] 和 dp[i+m][j+m] = 1,所以轉移方程就寫出來了,但是注意我們只能從後向前轉移,
也就是說我們一定要用選上一個數的狀態,因為這裡是01揹包,每一個數只能選一次,如果正著選就是完全揹包了。
還有就是可以作為母函式的入門題

#include <bits/stdc++.h>
#include <iostream>
#define X 10005
#define inf 0x3f3f3f3f
#define PI 3.141592653589793238462643383
#define IO  ios::sync_with_stdio(false),cin.tie(0), cout.tie(0);
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
typedef long long ll;
int dp[505][505];
int main()
{
    int n,k,num;
    cin>>n>>k;
    memset(dp,0,sizeof(dp));
    dp[0][0] = 1;
    for(int p=0;p<n;++p)
    {
        cin>>num;
        for(int i = k; i >= num; i--)
        {
            for(int j = 0; j <= k-num; j++)
                if(dp[i-num][j])
                    dp[i][j] = dp[i][j+num] = 1;
        }
    }
    int ans = 0;
    int cnt[555];
    for(int i = 0; i <= k; i++)
    {
        if(dp[k][i])
            cnt[ans++] = i;
    }
    cout<<ans<<endl;
        for(int i=0;i<ans;++i)
         (i==0?(cout<<cnt[i]):(cout<<' '<<cnt[i]));
        cout<<endl;
    return 0;
}

429B 
有一個矩陣,一個人從左上往右下走,一個人從左下往右上走,中間有一次相遇該點的值不算,求最後兩人走過路線值之和的最大值(兩人重複走過的點只計算一次)

記錄矩陣上的每個點分別到矩陣上四個角最大的值,假設某點是相遇點,
該路線的值就是該點到四個角上的最大值之和。因此遍歷矩陣上的每個點,求出最大值即可。

首先要保證只有一個格子重合,那麼只可能是以下兩種情況:
1) A向右走,相遇後繼續向右走,而B向上走,相遇後繼續向上走
2) A向下走,相遇後繼續向下走,而B向右走,相遇後繼續向右走
 

#include <bits/stdc++.h>
#include <iostream>
#define X 10005
#define inf 0x3f3f3f3f
#define PI 3.141592653589793238462643383
#define IO  ios::sync_with_stdio(false),cin.tie(0), cout.tie(0);
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
typedef long long ll;
ll a[1005][1005];
ll dp1[1005][1005];
ll dp2[1005][1005];
ll dp3[1005][1005];
ll dp4[1005][1005];
int main()
{
    int n,m;
    while(cin>>n>>m)
    {

        memset(dp1,0,sizeof(dp1)),memset(dp2,0,sizeof(dp2)),memset(dp3,0,sizeof(dp3)),memset(dp4,0,sizeof(dp4));
        for(int i=1;i<=n;++i)
            for(int j=1;j<=m;++j)cin>>a[i][j];
        //   右下
        for(int i=1;i<=n;++i)
            for(int j=1;j<=m;++j)
                dp1[i][j]=max(dp1[i][j-1],dp1[i-1][j])+a[i][j];
        // 右上
        for(int i=n;i>=1;--i)
            for(int j=1;j<=m;++j)
                dp2[i][j]=max(dp2[i][j-1],dp2[i+1][j])+a[i][j];
        //左下
        for(int i=1;i<=n;++i)
            for(int j=m;j>=1;--j)
            dp3[i][j]=max(dp3[i][j+1],dp3[i-1][j])+a[i][j];
        //右上
        for(int i=n;i>=1;--i)
            for(int j=m;j>=1;--j)
                dp4[i][j]=max(dp4[i+1][j],dp4[i][j+1])+a[i][j];

        ll ans=0;
        for(int i=2;i<n;++i)//你總不能然他倆在終點和出發點相遇吧
            for(int j=2;j<m;++j)
                {
                    ans=max(ans,dp1[i-1][j]+dp4[i+1][j]+dp2[i][j-1]+dp3[i][j+1]);
                    ans=max(ans,dp1[i][j-1]+dp4[i][j+1]+dp2[i+1][j]+dp3[i-1][j]);
                }
       cout<<ans<<endl;
    }
    return 0;
}