Aizuoj 0529

题目大意:给你n个得分值,让你最多投四次飞镖,问可以扎到的分数值之和小于m的最大值是多少(n<=1000)

    刚开始看到这道题以为是个背包看到数据范围m的值就知道背不动了...后来仔细想了一下,对于最多拿四个数来

讲,答案可能的来源有拿1个(1000种)、拿2个(1000^2种),拿3个(1000^3), 以及拿4个(1000^4种)。这

样直接枚举所有答案来源的话百分之九十九会超时。但是再想一下,对于拿3个来说,它必然可以转换成一种拿1个

+拿2个的和的状态,同理,拿4个可以转换成拿2个之和的状态。这样我们就可以暴力枚举出拿1个和拿2个的所有答

案数(10^6),然后对于这些状态可以稳定得到拿3个或者拿4个的状态。假如数组sum[]表示拿1个或2个的答案,那

么对于sum[i]来说,由它组成的3个或者4个的不小于m的最大值,sum[i]+sum[x]<m,这样只需要二分得到最大的x即可,

当然之前要对sum[]进行排序预处理,总的时间复杂度O(10^6*log(10^6)) ≈ 10^7。尝试写了一发果然过了。这道题一

开始没思路连题解都找不到,以后不能再懒了,多写博客,帮人帮己,学习使我快乐。

#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <queue>
using namespace std;
int nums[1100], sum[1100000];
int erfen(int i, int cnt, int m)
{
    int l = 1, r = cnt-1, Index = 0;
    while(l<=r)
    {
        int mid = (l+r)/2;
        if(sum[i]+sum[mid]<m)
        {
            Index = mid;
            l = mid+1;
        }
        else
            r = mid-1;
    }
    return Index;
}
int main()
{
    int n, m;
    while(scanf("%d %d", &n, &m), n||m)
    {
        for(int i=1; i<=n; i++)
            scanf("%d", &nums[i]);

        int cnt = 1;
        for(int i=1; i<=n; i++)///取一个或两个的所有可得到答案
        {
            sum[cnt++] = nums[i];///取一个
            for(int j=1; j<=n; j++)
                sum[cnt++] = nums[i]+nums[j];///取两个
        }

        sort(sum+1, sum+cnt);
        cnt = unique(sum+1, sum+cnt)-sum;

        int ans = 0;
        for(int i=1; i<cnt; i++)
        {
            if(sum[i]>=m)
                break;
            ans = max(ans, sum[i]);
            int Index = erfen(i, cnt, m);
            ans = max(ans, sum[i]+sum[Index]);
        }

        printf("%d\n", ans);
    }
    return 0;
}View Code

猜你喜欢

转载自www.cnblogs.com/zznulw/p/9027467.html