程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 整數劃分(三連發)

整數劃分(三連發)

編輯:C++入門知識

第一題:

1 樣例:如一個正整數5,可以劃分為7種:

[5]
[4,1]
[3,2] [3,1,1]
[2,2,1] [2,1,1,1]
[1,1,1,1,1]
2分析:2
將一個正整數n劃分,共有多少種劃分方式?
上面的例子第一行,所有加數不超過5;
第二行,所有加數不超過4;
........
第五行,所有加數不超過1.
定義 dp[n][m]表示正整數n,所有加數不超過m的劃分數目。

3分析m,n:
1、n== 1或m== 1時共1種劃分方式dp[n][m] = 1;
2、n== m時dp[n][n]= dp[n][m-1]+1,這樣就把主要問題轉化為對3的分解。
3、n> m時我們發現只要將dp[n][m-1]加上包含加數m的劃分數就等於dp[n][m]。即:dp[n][m]= dp[n][m-1] +包含加數m的劃分數。
包含加數m的劃分數可以轉化為:dp[n-m][m]。所以3可以表示為:
dp[n][m]= dp[n][m-1] + dp[n-m][m]
4、n< m等同於dp[n][n];

4代碼:
[cpp] view plaincopy
#include<iostream> 
#include<algorithm> 
#include<cstdio> 
#include<cstring> 
using namespace std; 
#define MAXN 1010 
 
int t , k; 
int dp[MAXN][MAXN]; 
 
void  solve(){ 
    memset(dp , 0 , sizeof(dp)); 
    for(int i = 1 ; i <= k ; i++){    
       for(int j = 1 ; j <= k ; j++){ 
          if(i == 1 || j == 1) 
            dp[i][j]  = 1; 
          else if(i == j)  
            dp[i][j] = dp[i][j-1] + 1; 
          else if(i < j) 
            dp[i][j] = dp[i][i]; 
          else 
            dp[i][j] = dp[i][j-1] + dp[i-j][j]; 
       }    
    } 
    printf("%d\n" , dp[k][k]); 

 
int main(){ 
    scanf("%d" , &t); 
    while(t--){ 
         scanf("%d" , &k); 
         solve(); 
    } 
    return 0; 

第二題:

1 樣例:5分成2個正整數的和有2種
               1 4
               2 3
2分析:
定義dp[n][m]表示的是將n分成m部分的方案數,那麼這個時候就考慮這個m部分的組成情況
1如果這m部分沒有1,那麼我們先將m個1分到m份上面,然後在將剩下的n-k分到m部分上,所以就有dp[n][m] = dp[n-m][m];
2如果這m部分有1,那麼先將1這個部分扣除,剩下的就是n-1分給m-1部分了,
所以就有dp[n][m] =dp[n-1][m-1];
所以就有dp[n][m]= dp[n-m][m] + dp[n-1]][m-1];
3注意事項:
當n= m時候dp[n][m]= 1;
當 m= 1時候dp[n][m]= 1;
當 n= 1時候n< m那麼dp[n][m]= 0;
4代碼:

[cpp] view plaincopy
#include<algorithm> 
#include<iostream> 
#include<cstdio> 
#include<cstring> 
using namespace std; 
#define MAXN 110 
 
int t , m , n; 
int dp[MAXN][MAXN]; 
 
void solve(){ 
     int i , j; 
     memset(dp , 0 , sizeof(dp)); 
     for(i = 1 ; i <= n ; i++) 
        dp[i][i] =  dp[i][1] = 1; 
     for(i = 1; i <= n ; i++){ 
        for(j = 1 ; j <= m ; j++){ 
           if(i <= j) 
             break; 
           if(i != 1 && j != 1) 
             dp[i][j] = dp[i-j][j] + dp[i-1][j-1]; 
        }   
     } 
     printf("%d\n" , dp[n][m]); 

 
int main(){ 
    scanf("%d" , &t); 
    while(t--){ 
        scanf("%d%d" , &n ,&m); 
        solve(); 
    } 

第三題:
1 分析:

第一問:

和第一題相同

第二問:

和第二題相同

第三問:

在第一問的基礎上大答案為dp[n][k];

第四問(不懂為什麼):

用dp[i][j]表示將i分成j個奇數的方案數,tmpDP[i][j]表示將i分成j個偶數的方案數

那麼就有tmpDP[i][j] = dp[i-j][j];  dp[i][j] = dp[i-1][j-1] + tmpDP[i-j][j];

第五問:

0/1背包的變形,有n件物品大小為1,2......n.求將大小為n的背包裝滿共有幾種方案數。

那麼就有狀態轉移方程: dp[i][j] = dp[i-1][j] + dp[i-1][j-i];  (j >= i)

                                             dp[i][j] = dp[i-1][j]    (j < i)

 

2代碼:

[cpp]
#include<algorithm> 
#include<iostream> 
#include<cstdio> 
#include<cstring> 
using namespace std; 
#define MAXN 110 
 
int n , k; 
long long dp[MAXN][MAXN]; 
long long ans; 
 
void solve(){ 
     int i , j; 
     /*第一個問題dp[i][j]表示將i分成不大於j的方案數*/ 
     memset(dp , 0 , sizeof(dp)); 
     for(i = 1 ; i <= n ; i++){ 
        for(j = 1; j <= n ; j++){ 
           if(i == 1 || j == 1)      
             dp[i][j] = 1; 
           else if(i == j) 
             dp[i][j] = 1 + dp[i][j-1]; 
           else if(i < j) 
             dp[i][j] = dp[i][i]; 
           else  
             dp[i][j] = dp[i][j-1] + dp[i-j][j]; 
        } 
     } 
     printf("%lld\n" , dp[n][n]); 
     ans = dp[n][k];/*保存下來作為第三問題的答案*/ 
     
     /*第二個問題dp[i][j]表示將i分成j部分的方案數*/ 
     memset(dp , 0 , sizeof(dp)); 
     for(i = 1 ; i <= n  ; i++) 
        dp[i][i] = dp[i][1] = 1; 
     for(i = 1 ; i <= n ; i++){ 
        for(j = 1 ; j <= k ; j++){ 
          if(i <= j) 
             break; 
          if(i != 1 && j != 1) 
             dp[i][j] = dp[i-j][j] + dp[i-1][j-1]; 
        } 
     } 
     printf("%lld\n%lld\n" , dp[n][k] , ans); 
     
     /*第四個問題(不大懂)*/ 
     long long tmpDP[MAXN][MAXN]; 
     memset(dp , 0 , sizeof(dp)); 
     memset(tmpDP , 0 , sizeof(tmpDP)); 
     dp[0][0] = tmpDP[0][0] = 1; 
     for(i = 1 ; i <= n ; i++){ 
        for(j = 1 ; j <= i ; j++){ 
           tmpDP[i][j] = dp[i-j][j]; 
           dp[i][j] = dp[i-1][j-1] + tmpDP[i-j][j]; 
        } 
     } 
     for(ans = 0 , i = 1 ; i <= n ; i++) 
         ans += dp[n][i]; 
     printf("%lld\n" , ans); 
     
     /*第五個問題dp[i][j]表示前i個物品中組成為j的方案數*/ 
     memset(dp , 0 , sizeof(dp));     
     for(i = 1 ; i <= n ; i++) 
         dp[i][0] =  dp[i][1] = 1;/*賦值初始化*/ 
     for(i = 1 ; i <= n ; i++){ 
        for(j = n ; j >= 2 ; j--){ 
          if(j >= i)  /*j >= i*/ 
             dp[i][j] = dp[i-1][j] + dp[i-1][j-i]; 
          else 
             dp[i][j] = dp[i-1][j]; /*j < i*/ 
        } 
     }      
    printf("%lld\n\n" , dp[n][n]);/*輸出答案*/ 

 
int main(){ 
    while(scanf("%d%d" , &n , &k) != EOF) 
        solve(); 
    return 0; 

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved