牛客 数划分 DP、DFS

6 篇文章 0 订阅

链接:https://ac.nowcoder.com/acm/problem/16695?&headNav=acm
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
将整数n分成k份,且每份不能为空,任意两份不能相同(不考虑顺序)。
例如:n=7,k=3,下面三种分法被认为是相同的。
1,1,5; 1,5,1; 5,1,1;
问有多少种不同的分法。
输入:n,k ( 6 < n ≤ 200,2 ≤ k ≤ 6 )
输出:一个整数,即不同的分法。
输入描述:
两个整数 n,k ( 6 < n ≤ 200, 2 ≤ k ≤ 6 )
输出描述:
1个整数,即不同的分法。
示例1
输入
7 3
输出
4

问题连接

问题分析

DP法的理解:
把n分成k份,情况一,可以先拿出一个1,再把剩下的n-1分成k-1份。情况二,要让第一个数大于1,也就是2起步,因为题意要求的划分是没有顺序的,所以其它位置也是2起步,那么可以看成是先让每个位置都放一个1,然后再进行划分,也就是把n-k分成k份,因为是用dp法,这个问题在之前就处理过了,所以引用之前的答案就可以了。在此之前,要把子问题的答案求出来,对于把i(i<=n)划分成j(j<=k),那么除了上面的两种情况,还有j>i的情况和i == j的情况。j>i,无法划分,故取0;i == j,就是把i划分成j个1,所以答案是1.
综上,
如果i==j,dp[i][j]=1;
如果i<j,dp[i][j]=0;
如果i>j,dp[i][j]=dp[i-1][k-1]+dp[i-j][k].

DFS法的理解:
每个位置,都放一个加数add,对总数计数,且为了不重复,往后的加数只能越来越大或者不变。如果能这样填满k个空位,得到n,那么这样的安排就是合理的,对应方法数+1。但其实,在第k-1的位置填完会就可以知道第k个位置能放的值,所以可以提前判断能否划分成功,并且以sum+add<=n,保证,第k个位置放的数不小于前面放过的数,这样就不会出现重复了。

代码如下

//dp法
#include<bits/stdc++.h>
using namespace std;

int dp[205][10];

int main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int n,k,i,j;
	cin>>n>>k;
	for(i=0;i<=n;i++)
	for(j=0;j<=k;j++){
		if(i==j) dp[i][i]=1;
		else if(i<j) dp[i][j]=0;
		else dp[i][j]=dp[i-1][j-1]+dp[i-j][j];
	}
	cout<<dp[n][k]<<endl;
return 0;
}
//dfs法
#include<bits/stdc++.h>
using namespace std;

int n,k,ans;
void dfs(int layer,int sum,int add){
	if(layer==k-1&&sum+add<=n){
		ans++;
		return;
	}
	for(int i=add;i<=n;i++){
		if(sum+i>=n) break;
		dfs(layer+1,sum+i,i);
	}
}
int main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>n>>k;
	ans=0;
	dfs(0,0,1);
	cout<<ans<<endl;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值