Bonetrousle HackerRank 数学 + 思维题

https://www.hackerrank.com/contests/world-codesprint-6/challenges/bonetrousle

给定一个数n,和k个数,1--k这k个,要求选择b个数,使得这b个数的和等于n。

首先考虑最小值,在1--k中选择前b个数,是最小的,记为mi。最大值,后b个数相加,记为mx

注意到一个东西:如果mi <= n <= mx。那么是绝对可行的。因为mi总能增加1(同时保证满足要求),所以在这个区间里面的,都是可行解。

所以首先从mi开始枚举,每次把第B个数变成最大值k(第b - 1个数改成k - 1....依次类推,因为不能重复用数字)

则总能枚举到mi >= n。然后输出方案即可。

bug点:后b个数太大了,会爆LL。所以,当后面的数相加到 >= n的时候,够了,证明n是 <= mx的了,有可行解。

然后对着模拟即可。复杂度O(b)

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;

#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
const int maxn = 1e5 + 20;
LL a[maxn];
void work () {
//    ios::sync_with_stdio(false);
    LL n, k, b;
//    scanf ("%lld%lld%lld", &n, &k, &b);
    cin >> n >> k >> b;
    //cout << n << " " << k << " " << b << endl;
    LL mi = 0, mx = 0;
    for (int i = 1; i <= b; ++i) {
        mi += i;
    }
    for (LL i = k; i >= k - b + 1; --i) {
        mx += i;
        //if (mx < 0) while (1);
        if (mx >= n) break; // ok
    }
    //cout << mi << " " << mx << endl;
    if (mx < n) {
        printf ("-1
");
        return ;
    }
    if (mi > n) {
        printf ("-1
");
        return ;
    }
    LL now = mi;
    int to = b;
    LL get = k;
    for (int i = 1; i <= b; ++i) a[i] = i;
    while (true) {
        now = now - a[to] + get;
        a[to] = get;
        if (now >= n) {
            LL cut = now - n;
            a[to] -= cut;
            for (int i = 1; i <= b - 1; ++i) {
                printf ("%lld ", a[i]);
            }
            printf ("%lld
", a[b]);
            break;
        }
        to--;
        get--;
    }
}

int main ()
{
#ifdef LOCAL
    freopen("data.txt","r",stdin);
#endif
    int t;
    cin >> t;
//    cout << t << endl;
    while (t--) work ();
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/liuweimingcprogram/p/5840606.html