1. 程式人生 > >hrbust 1216/哈理工oj 1216 數的劃分【dp】

hrbust 1216/哈理工oj 1216 數的劃分【dp】

數的劃分
Time Limit: 1000 MS Memory Limit: 65535 K
Total Submit: 184(89 users) Total Accepted: 125(87 users) Rating:  Special Judge: No
Description
將整數n分成k份,且每份不能為空,任意兩份不能相同(不考慮順序)。
例如:n=7,k=3,下面三種分法被認為是相同的。
1,1,5; 1,5,1; 5,1,1;
問有多少種不同的分法。
Input
有多則測試資料。
對於每組測試資料,僅有一行,包括兩個整數n,k (6<n<=200,2<=k<=6)。
Output
對於每組測試資料,輸出一個整數,即不同的分法。
Sample Input
7 3
Sample Output
4
Hint
輸入: 7 3
輸出:4 {四種分法為:1,1,5; 1,2,4; 1,3,3; 2,2,3;}
Source
NOIp2001高中組
Recommend
黃李龍

第一眼看到這個題,最基礎的思路有兩個,一個是dp,估計推出不來,一個是搜尋找解,因為資料不是很大,也許交一發超時實在不行還可以打表。

然後默默的dfs,也解出了樣例。但是在測試資料的時候整個人都崩潰了、輸入 200 6的時候,等待是很漫長的、、、、、剪枝無果,dp找思路。找啊找啊找思路,無果,百度數的劃分,最後找到了明確的解釋。

dp的思路是這樣形成的:

考慮兩個極端,一個是不要1的情況,一個是至少要一個1的情況(也可以理解為要1的情況)、

如果我們規定dp【i】【j】表示資料i分成j個數據有多少方法的話,我們的dp【i-1】【j-1】就是至少要一個1的情況(要1的情況)、那麼dp【i-j】【j】就是不要1的情況。

後邊這半句話可能理解起來費點勁。假如我們拿樣例來說話,根據剛剛的思路寫出這樣的等式:

dp【7】【3】=dp【4】【3】+dp【6】【2】、假設我們這裡知道dp【4】【3】和dp【6】【2】。後邊的dp【6】【2】很好理解,在6的基礎上加1,同時相當於7分解成6和1、但是前邊我們怎麼理解呢?我們這樣理解這個問題:7個小球放在3個盒子裡,不要有任何一個盒子只有一個小球,這個時候我們可以先拿出3個小球,然後任意分配4個小球到三個盒子裡,然後我們手裡的三個小球,每一個盒子裡都放下一個小球,這樣就形成了不會有哪個盒子只有一個小球的情況,所以這裡我們就能得到這樣的動態規劃方程:
dp【i】【j】=dp【i-j】【j】+dp【i-1】【j-1】;

最後上完整的AC程式碼:

#include <stdio.h>
#include <string.h>
using namespace  std;
int dp[205][7];
int main()
{
    memset(dp, 0, sizeof(dp));
    dp[0][0] = 1;
    for (int i = 1; i <= 205; i++)
    {
        for (int j = 1; j <= 6; j++)
        {
            if (i >= j)
            {
                dp[i][j] = dp[i-j][j] + dp[i-1][j-1];
            }
        }
    }
    int n, k;
    while (~scanf("%d%d", &n, &k))
    {
        printf("%d\n", dp[n][k]);
    }
    return 0;
}