洛谷 P1025 数的划分

10 篇文章 0 订阅

题目描述

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

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

1,1,51,1,5;
1,5,11,5,1;
5,1,15,1,1.

问有多少种不同的分法。

输入输出格式

输入格式:

 

n,kn,k (6<n \le 2006<n≤200,2 \le k \le 62≤k≤6)

 

输出格式:

 

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

 

输入输出样例

输入样例#1: 

7 3

输出样例#1: 

4

说明

四种分法为:
1,1,51,1,5;
1,2,41,2,4;
1,3,31,3,3;
2,2,32,2,3.

解法:

很直观的dfs。但题目要求是求不同的分法,所以这里学一个小技巧。

按照值的大小由小到大选取,每次dfs时记录上次选取的数。这个剪枝可以很好的避免超时。

#include <bits/stdc++.h>
#define pii pair<int, int>
#define ll long long
using namespace std;
const int maxn = 2e4 + 10;
int cnt;
int n, k;
set<multiset<int> > q;
multiset<int> a;
void solve(int last, int n, int k)
{
    if(n < 0) return;
    if(k == 0 && n != 0) return;
    if(n == 0 && k == 0)
    {
        cnt++;
        return;
    }
    for(int i = last; i * k <= n; i++)
    {
        solve(i, n - i, k - 1);
    }
}
int main()
{
    //    freopen("/Users/vector/Desktop/testdata.in", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> k;
    solve(1, n, k);
    cout << cnt << endl;
    return 0;
}

解法二:递推

dfs虽然好理解 但只能处理数据范围小的情况。如果数据范围一大就必须用递推的方法。

于是我们用状态dp[i][j]表示将i个数划分为j段的方案数。

我们可以将题目转化为将n个小球放入k个盒子中并且没有盒子为空的情况数。

所有的可能性等于至少有一个盒子有一个球和没有一个盒子有一个球。

这样进行划分是因为这种分类可以使都有能写出来的表达式

状态转移是从将i-1个球分为j-1个盒子和将i-j个球分为j段转移而来

dp[i][j] = dp[i - 1][j - 1] + dp[i - j][j]

#include <bits/stdc++.h>
#define pii pair<int, int>
#define ll long long
using namespace std;
const int maxn = 2e4 + 10;
int cnt;
int n, k;
int dp[200][200];//dp[i][j]把i分为j段
int main()
{
    //    freopen("/Users/vector/Desktop/testdata.in", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> k;
    //递推
    dp[1][1] = 1;
    for(int i = 2; i <= n; i++)
    {
        dp[i][1] = 1;
        for(int j = 2; j <= i; j++)
            dp[i][j] = dp[i - 1][j - 1] + dp[i - j][j];
    }
    cout << dp[n][k] << endl;
    return 0;
}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值