[dp算法]逃離農(nóng)場

牛牛在農(nóng)場飼養(yǎng)了n只奶牛,依次編號為0到n-1, 牛牛的好朋友羊羊幫牛牛照看著農(nóng)場.有一天羊羊看到農(nóng)場中逃走了k只奶牛,但是他只會告訴牛牛逃走的k只奶牛的編號之和能被n整除茫蛹。你現(xiàn)在需要幫牛牛計算有多少種不同的逃走的奶牛群。因為結(jié)果可能很大,輸出結(jié)果對1,000,000,007取模奈偏。
例如n = 7 k = 4:
7只奶牛依次編號為0到6, 逃走了4只
編號和為7的有:{0, 1, 2, 4}
編號和為14的有:{0, 3, 5, 6}, {1, 2, 5, 6}, {1, 3, 4, 6},{2, 3, 4, 5}
4只牛的編號和不會大于18,所以輸出5.

輸入描述:
輸入包括一行,兩個整數(shù)n和k(1 ≤ n ≤ 1000),(1 ≤ k ≤ 50),以空格分割鲫售。

輸出描述:
輸出一個整數(shù)表示題設(shè)所求的種數(shù)宦芦。

輸入例子:
7 4

輸出例子:
5

看了下網(wǎng)的一些解法,基本的dp狀態(tài)方程都是可以寫對的萍虽。問題再于實現(xiàn)時候的上來就使用狀態(tài)壓縮了,而且也沒解釋明白形真,看得一頭霧水杉编。這里寫一下過程
首先定義
使用dp[i][j][t]表示前i+1頭奶牛中選取j頭的和除以n余為t的方案數(shù)超全。

  1. 這里是i+1頭,所以i的取值范圍是0~n-1的邓馒。
  2. j的取值是可以取0的嘶朱,也就是一頭都沒逃走,j在循環(huán)里面的最大值是i和k的最小者光酣。

好了疏遏,接下來是狀態(tài)方程
則方案分為兩種:選取了第i頭奶牛和沒有選取第i頭奶牛兩個子問題

沒有選擇第i頭奶牛的方案數(shù): dp[i?1][j][t]
選擇第i頭奶牛的方案數(shù):dp[i?1][j?1][((t+n)?i)%n]

解釋一下這個((t+n)?i)%n,第一救军,因為選了第i頭牛财异,所以剩下的余數(shù)應該是用t-i,但是因為可能會出現(xiàn)負數(shù)唱遭,所以再加個n戳寸。然后再用n取余,就可以得到減去i之后拷泽,余數(shù)應該是多大疫鹊。

下面先不用狀態(tài)壓縮來實現(xiàn)算法:

int func(int n, int k) {
    vector<vector<vector<int>>> dp;
    vector<int> vec1;
    vector<vector<int>> vec2;
    vec1.resize(n); //因為只能取0~n-1共n個數(shù)
    vec2.resize(k + 1, vec1); //可以取0~k共k+1個數(shù)
    dp.resize(n, vec2); //因為只能取0~n-1共n個數(shù)
    
    for (int m=0; m<n; m++) {//先初始化全部元素都為0
        for (int i = 0; i<k+1; i++) {
            for (int j=0; j<n; j++) {
                dp[m][i][j] = 0;
            }
        }
    }
    
    //初始賦值 ,i = 0時, 存在1頭牛司致,選出0個拆吆,余n為0,這是一種唯一方案脂矫; 選出1個锈拨,余n為0,也是一種唯一方案
    dp[0][1][0] = dp[0][0][0] = 1;
    
    for (auto i = 1; i < n; i++) {//前i+1頭奶牛
    {
        for (auto j = 0; j <= MIN(k, i+1); j++)//選擇j頭奶牛羹唠。因為i對應有i+1頭牛
        {
            for (auto t = 0; t < n; ++t)//余t
            {
                if (j > 0) { //因為這里會涉及到 j-1奕枢,需要確保它打于0
                    dp[i][j][t] = (dp[i-1][j][t] + dp[i-1][j - 1][((t + n) - i) % n]);
                } else {
                    if (t == 0) { //當j==0的時候,也就是一頭牛都沒逃走佩微,那么這個時候余數(shù)必須是0才有值缝彬,取其他余數(shù)都是0
                        dp[i][j][t] = 1;
                    }
                }
            }
        }
    }
    
    return dp[n-1][k][0];
}

這個算法沒啥好說的,就是按照狀態(tài)方程實現(xiàn)哺眯。然后留意一下初始化條件和j-1的狀態(tài)就行谷浅。

然后來看看狀態(tài)壓縮的。留意到狀態(tài)轉(zhuǎn)移方程奶卓,其實每次計算新的i的時候一疯,它只會用到i-1的二維數(shù)組的值。所以可以只使用一個二維數(shù)組去保存狀態(tài)值就行了夺姑。所以狀態(tài)方程可以寫成

dp[j][t] = (dp[j][t] + dp[j - 1][((t + n) - i) % n]);

因為賦值的時候是先取等號右邊的值去計算的墩邀,所以賦值語句是正確的。就像a=a+1盏浙,這個計算的a結(jié)果為原始的a+1一樣眉睹。
這里有一個問題就是dp[j - 1][((t + n) - i) % n]這一項荔茬,里面依賴了j-1這項,所以我們必須先計算j竹海,然后在計算j-1.否則的話慕蔚,如果先計算了j-1,那么這個j-1的所有項對應的i都會加1斋配,和j對應的i就對不上了孔飒。

int func2(int n, int k) {
    vector<vector<int>> dp;
    vector<int> vec;
    vec.resize(n + 1);
    dp.resize(k + 1, vec);
    
    for (int i = 0; i<k+1; i++) {
        for (int j=0; j<n; j++) {
            dp[i][j] = 0;
        }
    }
    dp[1][0] = dp[0][0] = 1; //網(wǎng)上有些寫法,只寫了dp[0][0] = 1其實是不對的
    for (auto i = 1; i < n; i++)//前i+1頭奶牛
    {
        for (auto j = MIN(k, i+1); j > 0; --j)//選擇j頭奶牛艰争。
// 需要留意的就是這個十偶,這里必須用遞減來做,因為狀態(tài)方程依賴于j-1园细,
//如果我們使用遞增惦积,就會先計算了j-1,然后再計算j猛频,這樣狮崩,j-1就會變成了下一個循環(huán)的i。
//比如現(xiàn)在正在計算i=2鹿寻,計算完了j-1之后睦柴,j-1所對應的元素的值都會增加到對應i=3,
//然后你再計算j的時候毡熏,就會用到i=3的j-1的項坦敌。那樣就不對了。
        {
            for (auto t = 0; t < n; ++t)//余t
            {
                dp[j][t] = (dp[j][t] + dp[j - 1][((t + n) - i) % n]) ;
            }
        }
    }
    return dp[k][0];
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末狱窘,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子财搁,更是在濱河造成了極大的恐慌蘸炸,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尖奔,死亡現(xiàn)場離奇詭異搭儒,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來柴墩,“玉大人,你說我怎么就攤上這事铃岔。” “怎么了丹弱?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵德撬,是天一觀的道長铲咨。 經(jīng)常有香客問我躲胳,道長蜓洪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任坯苹,我火速辦了婚禮隆檀,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘粹湃。我一直安慰自己恐仑,他們只是感情好,可當我...
    茶點故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布为鳄。 她就那樣靜靜地躺著裳仆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪孤钦。 梳的紋絲不亂的頭發(fā)上歧斟,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天,我揣著相機與錄音偏形,去河邊找鬼静袖。 笑死,一個胖子當著我的面吹牛俊扭,可吹牛的內(nèi)容都是我干的队橙。 我是一名探鬼主播,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼萨惑,長吁一口氣:“原來是場噩夢啊……” “哼捐康!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起庸蔼,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤吹由,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后朱嘴,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體倾鲫,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年萍嬉,在試婚紗的時候發(fā)現(xiàn)自己被綠了乌昔。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,747評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡壤追,死狀恐怖磕道,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情行冰,我是刑警寧澤溺蕉,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布伶丐,位于F島的核電站,受9級特大地震影響疯特,放射性物質(zhì)發(fā)生泄漏哗魂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一漓雅、第九天 我趴在偏房一處隱蔽的房頂上張望录别。 院中可真熱鬧,春花似錦邻吞、人聲如沸组题。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽崔列。三九已至,卻和暖如春旺遮,著一層夾襖步出監(jiān)牢的瞬間赵讯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工趣效, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留瘦癌,地道東北人。 一個月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓跷敬,卻偏偏與公主長得像讯私,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子西傀,可洞房花燭夜當晚...
    茶點故事閱讀 43,658評論 2 350

推薦閱讀更多精彩內(nèi)容

  • 樹形動態(tài)規(guī)劃斤寇,顧名思義就是樹+DP,先分別回顧一下基本內(nèi)容吧:動態(tài)規(guī)劃:問題可以分解成若干相互聯(lián)系的階段拥褂,在每一個...
    Mr_chong閱讀 1,478評論 0 2
  • 回溯算法 回溯法:也稱為試探法娘锁,它并不考慮問題規(guī)模的大小,而是從問題的最明顯的最小規(guī)模開始逐步求解出可能的答案饺鹃,并...
    fredal閱讀 13,636評論 0 89
  • 動態(tài)規(guī)劃 動態(tài)規(guī)劃算法莫秆, Dynamic Programming簡稱DP,通郴谙辏基于一個遞推公式及一個或多個初始狀態(tài)...
    御風逍遙閱讀 5,278評論 0 7
  • 更多干貨就在我的個人博客 BlackBlog.tech 歡迎關(guān)注镊屎!也可以關(guān)注我的csdn博客:黑哥的博客謝謝大家!...
    BlackBlog__閱讀 3,448評論 0 7
  • 不管未來有多長久茄螃,請珍惜相聚的每一刻缝驳;不管多少個春夏秋冬,我們是永遠的朋友. 同是天涯淪落人,相逢何必曾相識。 萬...
    八月長青閱讀 225評論 0 0