UVA 10581 Partitioning for fun and profit

Problem A: Partitioning for fun and profit

A partition of a positive integer number  m  into  n elements ( n  ≤  m ) is a sequence of positive numbers a1 ,..., an  such that  a1 +...+ an  =  m  and a1  ≤  a2  ≤ ... ≤  an . Your task is to find a partition of a number  m  which occupies the  k -th position in the lexicographically ordered sequence of all partitions of m  into  n  elements.

The lexicographic ordering among the partitions of a number is defined as follows. For two partitions a and bof m into n elements such that a = [a1,...,an] andb = [b1,...,bn] we have a  <  b if and only if there exists an 1 ≤ i ≤ n such that for all j < i we have aj = bj and ai < bi. The sequence of all partitions is ordered in increasing lexicographic order and at the first we have the following sequence 1, 1, ... 1, m-n+1.

The first line of input contains a number c giving the number of cases that follow. Each of the subsequent clines contains three numbers: 1 ≤ m ≤ 220, 1 ≤ n ≤ 10 and 1 ≤ k which is not bigger than the number of partitions of m into n elements.

For each input data set print the k-th partition of m into n elements. Each element of a partition is to be printed in a separate line.

Sample input

2
9 4 3 
10 10 1

Output for sample input

1
1
3
4
1
1
1
1
1
1
1
1
1
1

Author:  Adapted from VI AMPwPZ by P. Rudnicki


思路:首先预处理一下,用dp[i][j][k]表示拆分的和为i,拆分为j组,而且第一个数k的情况数。转移的时候就是dp[i+x][j+1][x] += dp[i][j][k] (x <= k) 。 求了一遍之后,预处理k的前缀和 , dp[i][j][k]就是 第一个数>=k的情况数了。 详细请看代码


代码:

#include <iostream>
#include <vector>
#include <algorithm>
#include <string.h>
#include <cstring>
#include <map>
#include <set>
#include <stdio.h>
#include <cmath>
#include <cassert>
#include <math.h>
#define rep(i,a,b) for(int i=(a);i<(b);++i)
#define rrep(i,b,a) for(int i = (b); i >= (a); --i)
#define clr(a,x) memset(a,(x),sizeof(a))
#define LL long long
#define eps 1e-9
#define mp make_pair
using namespace std;
const int maxn = 220 + 5;
LL dp[maxn][11][maxn];
int n,m;
LL K;

void pre_init()
{
    dp[1][1][1] = 1;
    rep(i,1,maxn-1) {
        dp[i][1][i] = 1;
        rep(j,1,10) {
            rep(k,1,i+1) {
                rep(x,1,k+1) {
                    if (i + x >= maxn) break;
                    dp[i+x][j+1][x] += dp[i][j][k];
                }
            }
        }
    }
    rep(i,0,maxn) {
        rep(j,0,11) {
            rrep(k,maxn-2,0) {
                dp[i][j][k] += dp[i][j][k+1];
            }
        }
    }
}

int ans[maxn];

void solve()
{
    ans[0] = 1;
    rep(i,1,n) {
        int j;
        for(j = ans[i-1]; j <= m; ++j) {
            m -= j;
            if (dp[m][n-i][j] >= K) break;
            K -= dp[m][n-i][j];
            m += j;
        }
        ans[i] = j;
    }
    ans[n] = m;
    rep(i,1,n+1) printf("%d\n",ans[i]);
}

int main()
{
   //Getinput(); return 0;
    #ifdef ACM
       // freopen("in.txt", "r", stdin);
       // freopen("out.txt","w",stdout);
    #endif // ACM
    pre_init();
    int T; cin >> T;
    rep(cas,1,T+1) {
        scanf("%d%d%lld",&m,&n,&K);
        solve();
    }
}










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值