P1025 数的划分(排列组合问题)

目录

DP

DFS


题目描述

将整数n分成k份,且每份不能为空,任意两个方案不相同(不考虑顺序)。

例如:n=7,k=3,下面三种分法被认为是相同的。

1,1,5;
1,5,1;
5,1,1.

问有多少种不同的分法。

输入输出格式

输入格式:

n,k (6<n≤200,2≤k≤6)

扫描二维码关注公众号,回复: 5396051 查看本文章

输出格式:

1个整数,即不同的分法。

输入输出样例

输入样例#1: 复制

7 3

输出样例#1: 复制

4

说明

四种分法为:
1,1,5
1,2,4;
1,3,3;
2,2,3.


DP

f[ i ][ x ] 表示 i 分成 x 个非空的数的方案数,

当 i < x 时,f[ i ][ x ]=0,当 i==x 时,f[ i ][ x ]=1

当 x 为1的时候,就是只用分一份的时候 f 的值就是1

关键是要知道如下情况:

  • 有1的情况,就是确定了有一段的长度为1,此时方案数: f[i-1][x-1]
  • 此时就相当于有一个抽屉已经确定了并且那个抽屉里就是1,所以这个时候分法就等于把剩下的 i-1 个数分在剩下的 x-1段(抽屉苹果都被占了一个)的分法
  • 第二种情况是,不含1,此时方案数:f[i-x][x]       此时i必须要大于x
  • 没1的时候和挡板法的情况类似,因为没有一个抽屉是1,所以可以往每个抽屉里的都先放上一个1,总方法数就相当于把剩下的数(i-x)分到x个抽屉里,所以就得到了这样的状态转移方程。

状态转移方程:  f[i][x]=f[i-1][x-1]+f[i-x][x]


代码:

#include<iostream>
using namespace std;
int n,k;
int f[210][7];
int main()
{
	ios::sync_with_stdio(false);
	cin>>n>>k;
	for(int i=1;i<=n;i++)	f[i][1]=1;
	for(int i=1;i<=n;i++)
		for(int x=2;x<=k;x++)//x从2开始 
		{
			if(i>x) f[i][x]=f[i-1][x-1]+f[i-x][x];
			else	f[i][x]=f[i-1][x-1]; 
		}
	cout<<f[n][k];
	return 0;
}

 


DFS

就注意一个点:如果不剪枝,不单单是TLE的问题,连正确答案都出不来,会重复计算

代码:

#include<iostream>
using namespace std;
int n,k;
int sum;
void dfs(int step,int num,int rest)
{
	if(rest==1)//当只剩下一段待分了,可以认为已经分完 
	{	sum++;return;}
/*	同时循环最大只能进行到t/s,
避免出现因前面的数过大而导致后面的数无法取的情况。*/
	//for(int i=step;i<=num;i++)
//剪枝:不需要枚举到num,否则既有可能重复计算,又会加大运算
	for(int i=step;i<=num/rest;i++) //剪枝 i<=num/rest
		dfs(i,num-i,rest-1); 
}

int main()
{
	ios::sync_with_stdio(false);
	cin>>n>>k;
	dfs(1,n,k);
	cout<<sum<<endl;
	return 0; 
} 

猜你喜欢

转载自blog.csdn.net/qq_40663810/article/details/87639560