题目描述:地址:http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=2157
题目自己看,很简单的游戏,从n个数中取不超过四个的数,使得这几个数的和最接近给定的数m
这道题属于简单提,网上也有分析,我主要提醒一下几点:
1.一定要注意是不超过四个数,那么就可以是一个.....到四个
2.纯暴力会超时,暴力+技巧才会AC
怎么才算是有技巧呢?我的做法是这样的,将待挑选的数及任意两个数的和存贮到一个数组里面,然后对每一个sum[i]数用二分查找法
查找另一个符合要求的数sum[j]使得sum[i]+sum[j]<m而且比曾经出现过的最大数大,那么就更新结果值.
需要注意的是,以为数组里面存贮的是一个数和任意两个数字的和,那么组合情况只有三种: 两个数,三个数,四个数,没有一个数(自己好好想一下为什么)
那么我们就需要判断有时候取一个数也可能满足要求的情况,那么我们可以在输入的时候判断符合要求的数,并暂时存贮到结果变量中取
后来,路东方同学提醒我说,可以让第一个数为0 ,那么,就可以出现所有的组合了,而且也不会漏掉情况了,我恍然大悟,明白了.有兴趣的同学可以自己修改一下,
我下面的程序与并没有用他的做法,稍微麻烦了一下,多了一个判断语句
下面给出源代码:
#include <stdio.h>
#include <algorithm>
#include <iostream>
using namespace std;
int sum[5555555];
int main()
{
int n,m,ca=1;
while(scanf("%d%d",&n,&m)&&(n||m))
{
printf("Case %d: ",ca++);
int i,j,aim=0;
for(i=0;i<n;i++) //输入数据
{
scanf("%d",&sum[i]);
if(sum[i]<=m&&sum[i]>=aim) //判断这个数据是否符合要求
aim=sum[i];
}
int len=n;
for(i=0;i<n;i++)
for(j=0;j<n;j++)
sum[len++]=sum[i]+sum[j]; //求任意两个数的和
sort(sum,sum+len);
for(i=0;i<len;i++)
{
int l=0,h=len,mi;
while(l<h) //对当前的数用二分查找法查找另一个满足要求的数
{
mi=(l+h)/2;
if(sum[mi]+sum[i]<=m)
{
if(sum[i]+sum[mi]>=aim)
aim=sum[i]+sum[mi];
l=mi+1;
}
else
h=mi;
}
}
printf("%d\n\n",aim);
}
return 0;
}