1. 程式人生 > >劃分數系列問題

劃分數系列問題

blank post print creat 最小值 def itl fine 再次

整數劃分 --- 一個老生長談的問題:


  1) 練練組合數學能力.
  2) 練練遞歸思想
  3) 練練DP


  總之是一道經典的不能再經典的題目:

  這道好題求:

  1. 將n劃分成若幹正整數之和的劃分數。
  2. 將n劃分成k個正整數之和的劃分數。
  3. 將n劃分成最大數不超過k的劃分數。
  4. 將n劃分成若幹奇正整數之和的劃分數。
  5. 將n劃分成若幹不同整數之和的劃分數。

1.將n劃分成不大於m的劃分法:

  1).若是劃分多個整數可以存在相同的:

   dp[n][m]= dp[n][m-1]+ dp[n-m][m] dp[n][m]表示整數 n 的劃分中,每個數不大於 m 的劃分數。

  則劃分數可以分為兩種情況:
  a.劃分中每個數都小於 m,相當於每個數不大於 m- 1, 故劃分數為 dp[n][m-1].
   b.劃分中有一個數為 m. 那就在 n中減去 m ,剩下的就相當於把 n-m 進行劃分, 故劃分數為 dp[n-m][m];

  2).若是劃分多個不同的整數:

   dp[n][m]= dp[n][m-1]+ dp[n-m][m-1] dp[n][m]表示整數 n 的劃分中,每個數不大於 m 的劃分數。
   同樣劃分情況分為兩種情況:
  a.劃分中每個數都小於m,相當於每個數不大於 m-1,劃分數為 dp[n][m-1].

  b.劃分中有一個數為 m.在n中減去m,剩下相當對n-m進行劃分,

   並且每一個數不大於m-1,故劃分數為 dp[n-m][m-1]

2.將n劃分成k個數的劃分法:

方法可以分為兩類:dp[n][k]= dp[n-k][k]+ dp[n-1][k-1];

    第一類: n 份中不包含 1 的分法,為保證每份都 >= 2,可以先拿出 k 個 1 分
  到每一份,然後再把剩下的 n- k 分成 k 份即可,分法有: dp[n-k][k]
     第二類: n 份中至少有一份為 1 的分法,可以先那出一個 1 作為單獨的1份,剩
  下的 n- 1 再分成 k- 1 份即可,分法有:dp[n-1][k-1]

另一種方式:

dp[i,j]表示將i分成j份的方案數。

dp[i,j]:=dp[i-j,1]+dp[i-j,2]+dp[i-j,3]+…+dp[i-j,j-1]+dp[i-j,j];

時間復雜度是n*k^2。O(n*k)的方法:

由於,

dp[i,j]=dp[i-j,1]+dp[i-j,2]+…+dp[i-j,j];

dp[i-1,j-1]=dp[(i-1)-(j-1),1]+dp[(i-1)-(j-1),2]+…+dp[(i-1)-(j-1),j-1]

=dp[i-j,1]+dp[i-j,2]+…+dp[i-j,j-1];

因此,

dp[i,j]=dp[i-j,1]+dp[i-j,2]+…+dp[i-j,j-1]+dp[i-j,j]

=dp[i-1,j-1]+dp[i-j,j];

  hzwer思路 Orz

就是它這個分法比較特殊 不是一堆一堆分的
而是每次把每一堆+1,或者把空堆變成1

拓展:還有一個劃分為不超過k組的問題

就是看成與本題類似,但是可以有元素為0

得到:dp[i,j]=dp[i-j,j]+dp[i,j-1]

區別就是dp[i,j-1]中i沒有-1

參照hzwer的思想,因為元素可以為0,就算某一堆為空堆,總數也不需要-1

3.將n劃分成若幹奇數的劃分法:(不懂)

g[i][j]:將i劃分為j個偶數

f[i][j]:將i劃分為j個奇數

g[i][j] = f[i - j][j];

f[i][j] = f[i - 1][j - 1] + g[i - j][j];

  代碼如下所示:

技術分享
 1 /*
 2  * hit1402.c
 3  *
 4  *  Created on: 2011-10-11
 5  *      Author: bjfuwangzhu
 6  */
 7 
 8 #include<stdio.h>
 9 #include<string.h>
10 #define nmax 51
11 int num[nmax][nmax]; //將i劃分為不大於j的個數
12 int num1[nmax][nmax]; //將i劃分為不大於j的不同的數
13 int num2[nmax][nmax]; //將i劃分為j個數
14 int f[nmax][nmax]; //將i劃分為j個奇數
15 int g[nmax][nmax]; //將i劃分為j個偶數
16 void init() {
17     int i, j;
18     for (i = 0; i < nmax; i++) {
19         num[i][0] = 0, num[0][i] = 0, num1[i][0] = 0, num1[0][i] = 0, num2[i][0] =
20                 0, num2[0][i] = 0;
21     }
22     for (i = 1; i < nmax; i++) {
23         for (j = 1; j < nmax; j++) {
24             if (i < j) {
25                 num[i][j] = num[i][i];
26                 num1[i][j] = num1[i][i];
27                 num2[i][j] = 0;
28             } else if (i == j) {
29                 num[i][j] = num[i][j - 1] + 1;
30                 num1[i][j] = num1[i][j - 1] + 1;
31                 num2[i][j] = 1;
32 
33             } else {
34                 num[i][j] = num[i][j - 1] + num[i - j][j];
35                 num1[i][j] = num1[i][j - 1] + num1[i - j][j - 1];
36                 num2[i][j] = num2[i - 1][j - 1] + num2[i - j][j];
37             }
38         }
39     }
40     f[0][0] = 1, g[0][0] = 1;
41     for (i = 1; i < nmax; i++) {
42         for (j = 1; j <= i; j++) {
43             g[i][j] = f[i - j][j];
44             f[i][j] = f[i - 1][j - 1] + g[i - j][j];
45         }
46     }
47 }
48 int main() {
49 #ifndef ONLINE_JUDGE
50     freopen("data.in", "r", stdin);
51 #endif
52     int n, k, i, res0, res1, res2, res3, res4;
53     init();
54     while (~scanf("%d %d", &n, &k)) {
55         res0 = num[n][n];
56         res1 = num2[n][k];
57         res2 = num[n][k];
58         for (i = 0, res3 = 0; i <= n; i++) {
59             res3 += f[n][i];
60         }
61         res4 = num1[n][n];
62         printf("%d\n%d\n%d\n%d\n%d\n\n", res0, res1, res2, res3, res4);
63     }
64     return 0;
65 }
技術分享

4.將正整數劃分成連續的正整數之和

如15可以劃分成4種連續整數相加的形式:

15

7 8

4 5 6

1 2 3 4 5

首先考慮一般的形式,設n為被劃分的正整數,x為劃分後最小的整數,如果n有一種劃分,那麽

結果就是x,如果有兩種劃分,就是x和x x + 1, 如果有m種劃分,就是 x 、x x + 1 、 x x + 1 x + 2 、... 、x x + 1 x + 2 ... x + m - 1

將每一個結果相加得到一個公式(i * x + i * (i - 1) / 2) = n,i為當前劃分後相加的正整數個數。

滿足條件的劃分就是使x為正整數的所有情況。

如上例,當i = 1時,即劃分成一個正整數時,x = 15, 當i = 2時, x = 7。

當x = 3時,x = 4, 當x = 4時,4/9,不是正整數,因此,15不可能劃分成4個正整數相加。

當x = 5時,x = 1。


這裏還有一個問題,這個i的最大值是多少?

不過有一點可以肯定,它一定比n小。我們可以做一個假設,
假設n可以拆成最小值為1的劃分,如上例中的1 2 3 4 5。這是n的最大數目的劃分。如果不滿足這個假設,
那麽 i 一定比這個劃分中的正整數個數小。因此可以得到這樣一個公式i * (i + 1) / 2 <= n,即當i滿足
這個公式時n才可能被劃分。

代碼如下:

技術分享
 1 void split(int n) {
 2     int i, j, te, x, xlen;
 3     for (i = 1, xlen = 0; (te = i * (i - 1) / 2) < n; i++) {
 4         x = n - te;
 5         if (x % i == 0) {
 6             x /= i;
 7             printf("%d", x);
 8             for (j = 1; j < i; j++) {
 9                 printf("%d ", x + j);
10             }
11             printf("\n");
12             xlen++;
13         }
14     }
15     printf("%d\n", xlen);
16 }
技術分享

5.以下是轉載的:

  求劃分因子乘積最大的一個劃分及此乘積  

  問題簡述:給定一個正整數n, 則在n所有的劃分中, 求因子乘積最大的一個劃分及此乘積。例如:8 = {8}, {7, 1}, {6, 2}, {5, 3}, {4, 4}, {3, 3, 2}, {2, 2, 2, 2} 等,那麽在這些當中,3 * 3 * 2 的乘積最大,所以輸出整個劃分

和這個乘積 18。
  算法分析:這是我在某個論壇上看到的問題,以及別人針對此問題的數學分析,現簡單的整理如下:
  (1)對於任意大於等於4的正整數m, 存在一個劃分m = m1+m2, 使 m1*m2 >= m證: 令m1 = int(m/2), 則 m1 >= 2 , m2 = m-m1; 那麽m2 > 2,並且 m2 >= m/2 >= m1; m1*m2 >= 2*m2 >= m; 證畢;
該證明簡單的來說就是:對於一個大於等於4的正整數m,存在一個2塊劃分的因子,這兩個因子的乘積總是不小於原數m本身。
  (2)由(1)知此數最終可以分解為 2^r * 3^s。現證明 r <= 2;
  證:若r > 2, 則至少有3個因子為2, 而2*2*2 < 3*3;
  所以可以將3個為2的因子,換為兩個因子3;積更大;證畢。
  綜合(1),(2),則有:任何大於4的因子都可以有更好的分解, 而4可以分解為2*2。
  所以:此數應該分解為 2^k1 * 3^k2。而且可以證明 k1>=0 並且 k1 <= 2,因此:
   A.當n = 3*r 時, 分解為 3^r
   B.當n = 3*r+1時, 分解為 3^(r-1)*2*2
  C.當n = 3*r+2時, 分解為 3^r*2
  剩下編程處理,那就是太簡單了,首先是處理 <= 4的特殊情況,再對>4的情況進行模3的3種情況的判斷,最後一一輸出。可見,數學在整數劃分問題上有太強的功能。誰叫這個問題叫整數劃分呢,不與數學密切才怪! ^_^。

6.小學六年級奧數---整數劃分(有用結論)

  例1:把14分拆成若幹個自然數的和,再求出這些數的積,要使得到的積最大,應該把14如何分拆?這個最大的乘積是多少?

  分析與解:我們先考慮分成哪些數時乘積才能盡可能地大。
  首先,分成的數中不能有1,這是顯然的。
  其次,分成的數中不能有大於4的數,否則可以將這個數再分拆成2與另外一個數的和,這兩個數的乘積一定比原數大,例如7就比它分拆成的2和5的乘積小。
  再次,因為4=2×2,故我們可以只考慮將數分拆成2和3。
  註意到2+2+2=6,2×2×2=8;3+3=6,3×3=9,因此分成的數中若有三個2,則不如換成兩個3,換句話說,分成的數中至多只能有兩個2,其余都是3。根據上面的討論,我們應該把14分拆成四個3與一個2之和,即14=3+3+3+3+2,這五數的積有最大值 3×3×3×3×2=162。
  將上述結論推廣為一般情形便是:
  把自然數S(S>1)分拆為若幹個自然數的和: S=a1+a2+…+an,則當a1,a2,…,an中至多有兩個2,其余都是3時,其連乘積m=a1a2…an有最大值。

  例2:把1993分拆成若幹個互不相等的自然數的和,且使這些自然數的乘積最大,該乘積是多少?


由於把1993分拆成若幹個互不相等的自然數的和的分法只有有限種,因而一定存在一種分法,使得這些自然數的乘積最大。

  若1作因數,則顯然乘積不會最大。把1993分拆成若幹個互不相等的自然數的和,因數個數越多,乘積越大。為了使因數個數盡可能地多,我們把1993分成2+3…+n直到和大於等於1993。

若和比1993大1,則因數個數至少減少1個,為了使乘積最大,應去掉最小的2,並將最後一個數(最大)加上1。

若和比1993大k(k≠1),則去掉等於k的那個數,便可使乘積最大。

所以n=63。因為2015-1993=22,所以應去掉22,把1993分成(2+3+…+21)+(23+24+…+63)這一形式時,

  這些數的乘積最大,其積為 2×3×…×21×23×24×…×63。

轉自http://www.cnblogs.com/radiumlrb/p/5797168.html

劃分數系列問題