原题链接:http://210.44.14.31/problem/show/1141
注意事项:
范围太大,爆搜超时。
本人猜测,本题的测试数据可能均是需要四个数才能的出最优解(依据第二个代码)。
具体分析看代码。
代码如下:
#include<iostream>
#include<algorithm>
#include<cstdio> //别用cin,本题测试数据较多
using namespace std;
int num[1000000 + 10];
int main()
{
int n, m;
int k = 0;
while (scanf_s("%d%d",&n,&m)&&(n||m))
{
num[0] = 0;
int max = 0;
for (int i = 1; i <= n; i++)
{
scanf_s("%d", &num[i]);
if (num[i]>m) //超范围的直接去除
{
n--;
i--;
continue;
}
} //此时num数组里有 1到n 个单个的值
int cnt = n+1;
cout << "Case " << ++k << ": ";
for (int i = 1; i <= n; i++)
for (int j = i; j <= n; j++)
if (num[i] + num[j]<=m)
num[cnt++] = num[i] + num[j];
//此时前n个位单个值,n+1到cnt为不大于m的所有两个值的和
sort(num, num + cnt);
int left, right=cnt-1,nextright=cnt-1;
for (int i = 0; i < cnt; i++) //因为数组里有单个值也有两个值的和 所以会出现 两个值的和、三个值的和、四个值的和
{
left = i;
while (left <= right)
{
int mid = (left + right) >> 1;
int tempmax = num[i] + num[mid];
if ( tempmax< m)
{
left = mid + 1;
if ( tempmax> max)
{
max = tempmax;
nextright = mid; //关键:在已经排好序的数组里,从i到i+1 i对应的mid必定在i+1对应的mid 的右边即mid(i)>=mid(i+1)这样做可以提高效率
}
}
else if (tempmax>m)
right = mid - 1;
else //找到直接结束循环
{
max = m;
i = cnt;
break;
}
}
right = nextright; //将right减小
}
cout << max << endl<<endl;
}
return 0;
}
附另解(本解法只用了四个数的组合便可ac,代码简单就不做详解了):
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int str[1000010],op[1010];
int main()
{
int i,j,k,n,m,ans,t=1;
bool flag=false;
while(~scanf("%d%d",&n,&m),n||m)
{
op[0] = k = 0;
for(i=1;i<=n;i++)
scanf("%d",&op[i]);
sort(op,op+n+1);
for(i=0;i<=n;i++)
for(j=i;j<=n;j++)
{
if(op[i]+op[j]<=m)
str[k++] = op[i]+op[j];
}
sort(str,str+k);
ans = 0;
for(i=0;i<k;i++)
{
int low=i,high=k-1;
while(low<high)
{
int mid=(low+high)/2;
if(str[i]+str[mid]>m)
high = mid-1;
else
low = mid+1;
}
if(str[i]+str[low]>ans && str[i]+str[low]<=m)
ans = str[i]+str[low];
}
if(flag)
printf("\n");
printf("Case %d: %d\n",t++,ans);
flag = true;
}
return 0;
}