1. 程式人生 > >劃分數

劃分數

劃分數  挑戰上的題

***************************************************************************************

注意是不超過

1、定義dp[i][j]=j的i的劃分總數  j的i劃分表示的意義為 j固定,i可以取到1-i。

2、遞推公式  dp[i][j]=dp[i][j-i]+dp[i-1][j];

3、分類討論:

      1.j >= i時,dp[i][j] = dp[i-1][j] ( j的i-1劃分,相當於當前位取0的全部情況 ) + dp[i][j-i](當前位不取0,先把每一個置為1,再將剩下的j-i分下去);

      2.j < i時,dp[i][j] = dp[i-1][j];  當前位只能取0。

例題:

郭姐最近很無聊,於是就開始分石頭。郭姐有n顆石頭,她要把石頭分成m堆,每堆至少有一個石子,她想知道有多少種分法

輸入描述

  • 2個整數,n和m(m <= n <= 20)。

輸出描述

  • 一個整數,表示種數

樣例輸入  7 3  樣例輸出  4

來源   2016年中北大學新生賽

提示   由於石頭都是一樣的,若僅僅只是順序不同,那還是同一種分法,比如把7分成3堆,(1,1,5)和(1,5,1)是同一種

注意是剛好m堆,每堆至少一個石子

上述提到的公式是適用於不超過m即空堆是小於m的數即可 所以注意最後答案是(看程式碼)

#include<cstdio>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<stdlib.h>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=1000000;
int dp[50][50];
int main(){
	int n,m;
	cin>>n>>m;
	dp[0][0]=1;
	for(int i=1;i<=m;i++){
		for(int j=0;j<=n;j++){
			if(j>=i){
				dp[i][j]=dp[i][j-i]+dp[i-1][j];
			}else
			dp[i][j]=dp[i-1][j];
		}
	}
	int ans=dp[m][n]-dp[m-1][n];//注意
	cout<<ans<<endl;
    return 0;
} 

牛客  放蘋果   一樣的題https://www.nowcoder.com/questionTerminal/bfd8234bb5e84be0b493656e390bdebf

不過是不超過M堆的情況;

#include<algorithm>
#include<iostream>
#include<stdlib.h>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=1000000;
int dp[50][50];
int main(){
	int n,m;
	cin>>m>>n;
	dp[0][0]=1;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=m;j++){
			if(j>=i)
			  dp[i][j]=dp[i][j-i]+dp[i-1][j];
			else
			  dp[i][j]=dp[i-1][j];  
		}
	}
	int ans=dp[n][m];
	cout<<ans<<endl;
	return 0;
}

遞迴也可以寫

牛客該題下討論有詳解,感興趣可以去學一下