【来源】
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。
【数据范围】
6 ≤ n ≤ 200 , 2 ≤ k ≤ 6 6≤n≤200, 2≤k≤6 6≤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】
动态规划。
显然, d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] + d p [ i − j ] [ j ] dp[i][j]=dp[i-1][j-1]+dp[i-j][j] dp[i][j]=dp[i−1][j−1]+dp[i−j][j]
- 当i=j时,1
- 当i<j时,0
- 当i>j时,分为两种情况
- 有1的:方案数为 f [ i − 1 ] [ j − 1 ] f[i-1][j-1] f[i−1][j−1]
- 没有1的:方案数为 f [ i − j ] [ j ] f[i-j][j] f[i−j][j]
所以,状态转移方程为: f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] + f [ i − j ] [ j ] f[i][j]=f[i-1][j-1]+f[i-j][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;
}