SDUT_Greatest_Number之折半查找解题报告

Greatest Number
Time Limit: 1000ms   Memory limit: 65536K  
题目描述
    Saya likes math, because she think math can make her cleverer.
    One day, Kudo invited a very simple game:
    Given N integers, then the players choose no more than four integers from them (can be repeated) and add them together. Finally, the one whose sum is the largest wins the game. It seems very simple, but there is one more condition: the sum shouldn’t larger than a number M.
    Saya is very interest in this game. She says that since the number of integers is finite, we can enumerate all the selecting and find the largest sum. Saya calls the largest sum Greatest Number (GN). After reflecting for a while, Saya declares that she found the GN and shows her answer.
    Kudo wants to know whether Saya’s answer is the best, so she comes to you for help.
    Can you help her to compute the GN?
输入
    The input consists of several test cases.
    The first line of input in each test case contains two integers N (0<N≤1000) and M(0<M≤ 1000000000), which represent the number of integers and the upper bound.
    Each of the next N lines contains the integers. (Not larger than 1000000000)
    The last case is followed by a line containing two zeros.
输出
    For each case, print the case number (1, 2 …) and the GN.
    Your output format should imitate the sample output. Print a blank line after each test case.
示例输入

2 10
100
2

0 0

示例输出

Case 1: 8

题目大意:
    输入N个数据,从中挑选最多4个,求其和,输出使该和不大于M的最大值。
思路解析:
    很多人一眼看来,将该题划为了背包问题来解决,想来确实跟01背包问题很相似,但我们注意到,题目输入的数据最多为1000,考虑到可以取0—4个四种情况,按照动态规划来做就会造成超时。同样的,如果我们把情况大体分为5类后,每一种情况都求出来,存入数组,然后找到满足题意的最大值,当然也是可以的,但是同样造成的结果也是超时。那么,我们所要做的当然就是在寻找高效算法的同时进行剪枝。首先,在输入数据时,对于本身已经大于M的数据我们不予存储,而后,把满足的数据两两相加,继续剪枝,将大于M的予以剔除,满足的存入同一数组,该数组的0地址我们存入0。接着,对该数组按从小到大排序,依次遍历,两两相加,(这里,我们显然不会挨个相加,所采用的方法就是折半查找,如果相加大于M,我们想要的数据显然应该在前半段,否则就应该在后半段,依次折半,不得不得不说复杂度减小了很多。)如此,我们就得到了0,1,2,3,4个数相加的各种情况,将满足的最大的数保存在MAX中即可。
代码如下:
#include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;

int data[6000000];

int main()
{
    int n,m,i,j,len,max,mid,sta,end,pre,temp;
    int Case=1;
    while(cin>>n>>m&&(n||m))
    {
        len=max=0;
        data[0]=0;
        for(i=1;i<=n;data[i]>m?n--:i++)  //输入数据,剪枝,检查输入数据若小于等于m,则存入数组
            cin>>data[i];
        len=n+1;            //len为p数组的长度
        for(i=1;i<=n;i++)    //继续剪枝,将输入两两相加后小于等于m的数据存入数组p
            for(j=i;j<=n;j++)
                if((temp=data[i]+data[j])<=m)
                    data[len++]=temp;
        sort(data,data+len);   //对p数组中数据从小到大排序
        pre=end=len-1;    //折半查找,pre、end表示数组首尾
        for(i=0;i<len;i++)
        {
            sta=i;
            while(sta<=end)   //对于每个数据,继续两两相加,将满足不大于m得最大结果保存在max中
            {
                mid=(sta+end)/2;
                temp=data[i]+data[mid];
                if(temp>m)
                    end=mid-1;
                else if(temp<m)
                {
                    sta=mid+1;
                    if(temp>max)
                    {
                        pre=mid;
                    max=temp;
                    }
                }
                else
                {
                    max=m;
                    i=len+1;
                    break;
                }
            }
            end=pre;
        }
        cout<<"Case "<<Case++<<": "<<max<<endl<<endl;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值