CFGym 101194D 题解

一、题目链接

  http://codeforces.com/gym/101194/problem/D

二、题意

  给定一个数字n和一个数字k,一个n个整数的序列,让你在里面找尽可能多的长度为k的符合“要求”的序列。一个数字不能被重复使用。

  要求:对于一个序列A0A1……An, 保证对任意的i(0 <= i < n),都有Ai * 2 <= A(i + 1)。

三、思路

  1、我一开始想到的方法是:把n个数排序,从大的数开始使用二分往前找,找到一个就标记一个数,在一次寻找中,如果能找到k个数,那么,结果+1,最后输出结果。这样的时间复杂度为:O(KNlogN),不会超时,但是,这样的想法是错的。比如,这样的样例:n = 6, k = 3, 440 220 110 105 40 2。同样地,从小往大找也是不行的,样例性质相同:n = 6, k = 3, 2 40 110 115 220 440。也就是说,从大往小找,对于第i个被找到的元素e,不应该找第一个小于等于e / 2的元素。所以,这种想法是直接跪在这个样例上了。注意,C++是没有内置的在有序表查找第一个小于等于给定值的函数,那么,我们可以把整个序列取为相反数(全部变成负),按升序排序,原本要查找小于等于就变成要查找大于等于了,就可以使用内置的lower_bound了。另外,要注意一点,现在二分的查找值是当前元素值a[i] / 2,因为原本数值越大的,取负后越小,越除则越大,最后相当于还是在求从大往小的。另外的话,负数除法,不要用右移运算符。

  2、第二种方法:对原数组直接排序(不用取相反数),二分最后的结果,然后对二分得到的中间值mid,使用排序后的数字序列的前mid个数当作mid个子序列的领头,表明我想测试下是否存在mid这么多个序列。然后,枚举每一个领头,都使用lower_bound去后面查找k - 1个符合“要求”的数。如果这mid个领头都能找到k个数来满足“要求”,那么,说明这个mid是可以的,否则说明不行。但是,这个看起来没错的想法,又会跪在这个样例上:n = 8, k = 4, 1 2 3 4 110 220 225 440。仔细想想,这其实只是把前一个想法往后移了,枚举每一个领头后,查找后面k-1个数的过程和前一个方法一样的。

  3、第三种方法:这个问题,实际上就相当于给mid列每列k个座位安排学生就坐,前面第二种方法,就是一列一列地排。而这种方法,就是一行一行地排,如果能排满所有座位,说明这个mid是可以的。另外的话,为什么是选前mid个当领头呢,这就贪心的思想了,如果选后面的数当领头,那选这前mid里面的数更好,因为他们更小。

四、源代码

  1、方法一(WA on test 2):

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 300010;
LL a[MAXN];
bool vis[MAXN];
int t, n, k;
vector<int> tmp;

int main() {
#ifndef ONLINE_JUDGE
    freopen("Dinput.txt", "r", stdin);
#endif // ONLINE_JUDGE
    int tc = 1;
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &k);
        for(int i = 1; i <= n; ++i) {
            scanf("%lld", a + i);
            a[i] = -a[i];
        }
        memset(vis, 0, sizeof(vis));
        int ans = 0;
        sort(a + 1, a + n + 1);
        for(int i = 1; i <= n; ++i) {
            if(vis[i])continue;
            int cnt = 1;
            int idx = i;
            for(int j = 1; j <= k - 1; ++j) {
                idx = lower_bound(a + 1, a + n + 1, a[idx] / 2) - a;
                while(idx <= n && vis[idx])idx++;
                if(idx <= n) {
                    cnt++;
                    vis[idx] = true;
                }
                if(idx > n)break;
            }
            if(cnt >= k)ans++;
        }
        printf("Case #%d: %d\n", tc++, ans);
    }
    return 0;
}

  2、方法二(WA on test 2):

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 300010;
LL a[MAXN];
bool vis[MAXN];
int t, n, k;

bool test(int ans) {
    int tmp = 0;
    memset(vis, 0, sizeof(vis));
    for(int i = 1; i <= ans; ++i) {
        if(vis[i])continue;
        int cnt = 1;
        int idx = i;
        for(int j = 1; j <= k - 1; ++j) {
            idx = lower_bound(a + ans + 1, a + n + 1, a[idx] * 2) - a;
            while(idx <= n && vis[idx])idx++;
            if(idx <= n) {
                cnt++;
                vis[idx] = true;
            }
            if(idx > n)break;
        }
        if(cnt >= k)tmp++;
    }
    if(tmp < ans)return false;
    else if(tmp >= ans)return true;
}

int main() {
#ifndef ONLINE_JUDGE
    //freopen("Dinput.txt", "r", stdin);
#endif // ONLINE_JUDGE
    int tc = 1;
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &k);
        for(int i = 1; i <= n; ++i) {
            scanf("%lld", a + i);
        }
        sort(a + 1, a + n + 1);
        int low = -1, high = n + 1, mid;
        while(low < high - 1) {
            mid = (high + low) / 2;
            if(test(mid)) low = mid;
            else high = mid;
        }
        printf("Case #%d: %d\n", tc++, low);
    }
    return 0;
}

  3、方法三(Accepted):

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 300010;
LL a[MAXN];
bool vis[MAXN];
int t, n, k;
vector<LL> vecs[MAXN];

bool test(int mid) {
    for(int i = 1; i <= n; ++i)vecs[i].clear();
    for(int i = 1; i <= mid; ++i)vecs[i].push_back(a[i]);
    int j = 1, sz = 0;
    for(int i = mid + 1; i <= n; ++i) {
        sz = vecs[j].size();
        if(a[i] >= 2 * vecs[j][sz - 1]) {
            vecs[j].push_back(a[i]);
            j++;
            if(j > mid)j = 1;
        }
    }
    for(int i = 1; i <= mid; ++i)if(vecs[i].size() < k)return false;
    return true;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("Dinput.txt", "r", stdin);
#endif // ONLINE_JUDGE
    int tc = 1;
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &k);
        for(int i = 1; i <= n; ++i)scanf("%lld", a + i);
        sort(a + 1, a + n + 1);
        int low = -1, high = n + 1, mid;
        while(low < high - 1) {
            mid = (high + low) / 2;
            if(test(mid)) low = mid;
            else high = mid;
        }
        printf("Case #%d: %d\n", tc++, low);
    }
    return 0;
}

  4、方法三——优化版:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 300010;
LL a[MAXN], b[MAXN];
int t, n, k;

bool test(int mid) {
    for(int i = 0;i < mid;++i)b[i] = a[i];
    int tmp = 1, j = 0;
    for(int i = mid; i < n; ++i) {
        if(a[i] >= b[j] * 2) {
            b[j++] = a[i];
            if(j >= mid) {
                ++tmp;
                j = 0;
            }
        }
    }
    return tmp >= k;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("Dinput.txt", "r", stdin);
#endif // ONLINE_JUDGE
    int tc = 1;
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &k);
        for(int i = 0; i < n; ++i)scanf("%lld", a + i);
        sort(a, a + n);
        int low = 0, high = n + 1, mid;
        while(low < high - 1) {
            mid = (high + low) / 2;
            if(test(mid)) low = mid;
            else high = mid;
        }
        printf("Case #%d: %d\n", tc++, low);
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/fuzhihong0917/p/7637616.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值