【C++】「一本通 1.3 例 1」「NOIP-2001」數的劃分

【來源】

NOIP-2001
一本通題庫-1440
LibreOJ-10018
HRBUST-1216
vjudge

注意:HRBUST和其他OJ的題目題意相同,但有多組數據。
本文題面以LibreOJ爲準,代碼通用。

【題目描述】

將整數 n 分成 k 份,且每份不能爲空,問有多少種不同的分法。當 n=7,k=3 時,下面三種分法被認爲是相同的:1,1,5; 1,5,1; 5,1,1

【輸入格式】

一行兩個數 n , k。

【輸出格式】

一行一個整數,即不同的分法數。

【樣例輸入】

7 3

【樣例輸出】

4

【樣例解釋】

四種分法爲:1,1,5;1,2,4;1,3,3;2,2,3。

【數據範圍】

6n200,2k66≤n≤200, 2≤k≤6

【解析1】

深搜剪枝。
求n無序劃分成k份的方案數。
在搜索中依次枚舉,然後判斷。
同時控制好節點的上界和下界,這樣就是剪枝。

【代碼1】

#pragma GCC optimize(3,"Ofast","inline")
#pragma G++ optimize(3,"Ofast","inline")

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>

#define RI                 register int
#define re(i,a,b)          for(RI i=a; i<=b; i++)
#define ms(i,a)            memset(a,i,sizeof(a))
#define MAX(a,b)           (((a)>(b)) ? (a):(b))
#define MIN(a,b)           (((a)<(b)) ? (a):(b))

using namespace std;

typedef long long LL;

const int N=205;
const int inf=1e9;

int n,m,ans;
int a[10];

void dfs(int x,int y,int z) {
	if(y==1 && x>=z) {
		ans++;
		return;
	} 
	for(int i=(z ? z : 1); i<=x; i++) 
		dfs(x-i,y-1,i);
}

int main() {
	while(scanf("%d%d",&n,&m)!=EOF) {
		ans=0;
		dfs(n,m,0);
		printf("%d\n",ans);
	}
	return 0;
}

【解析2】

動態規劃。

顯然,dp[i][j]=dp[i1][j1]+dp[ij][j]dp[i][j]=dp[i-1][j-1]+dp[i-j][j]

  • 當i=j時,1
  • 當i<j時,0
  • 當i>j時,分爲兩種情況
    • 有1的:方案數爲 f[i1][j1]f[i-1][j-1]
    • 沒有1的:方案數爲 f[ij][j]f[i-j][j]

所以,狀態轉移方程爲:f[i][j]=f[i1][j1]+f[ij][j]f[i][j]=f[i-1][j-1]+f[i-j][j]

【代碼2】

#pragma GCC optimize(3,"Ofast","inline")
#pragma G++ optimize(3,"Ofast","inline")

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>

#define RI                 register int
#define re(i,a,b)          for(RI i=a; i<=b; i++)
#define ms(i,a)            memset(a,i,sizeof(a))
#define MAX(a,b)           (((a)>(b)) ? (a):(b))
#define MIN(a,b)           (((a)<(b)) ? (a):(b))

using namespace std;

typedef long long LL;

const int N=205;
const int inf=1e9;

int n,k;
int f[N][10];

int main() {
	while(scanf("%d%d",&n,&k)!=EOF) {
		for(int i=0; i<=n; i++) for(int j=0; j<=MIN(i,k); j++) {
			if(j<2) {
				f[i][j]=1;
				continue;
			}
			f[i][j]=f[i-1][j-1];
			if(i>=j) f[i][j]+=f[i-j][j];
		}
		printf("%d\n",f[n][k]);
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章