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\n"); return ; } if (mi > n) { printf ("-1\n"); 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\n", 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; }
时间: 2024-10-09 21:22:14