题目链接:http://2012.nwerc.eu/en/results/problems/
题目大意: 先给出一个X,单位厘米
然后是n条长度为ai的边,单位纳米
任取两条边,使得相加后的长度等于X
求满足长度等于X且 | L1— L2 | 最大 (既长度只差的绝对值最小)
把两条边从小到大输出,不存在则输出“danger”
解题思路: 先把边从小到大排列,然后分别枚举每一条边
看能否找到另一条边与之相加等于X
把所有的情况都算出来,并且判断哪种情况边之差的绝对值最小
剪枝: 其实我们没有必要把所有的边都枚举一次,下面分情况讨论:
1.最长和次长的相加小于X,那么说明不存在,直接输出"danger"
2.第一条和最后一条边相加大于X,并且中间那条边乘与2小于X,只需要枚举大于中间边:
假设X=30,有七条边可供选择 3 5 7 13 15 20 27
中间的边 13*2=26<X,第1,2,3条边任意取两条边都不可能相加等于X
3.枚举边的时候从最长那条边开始,枚举完那条边可以把那条边去掉,既缩小二分的范围
英文版解题报告:
( 剪枝之后时间复杂度小于O(nlogn) )
代码:
#include <stdio.h>
#include <stdio.h>
#include <algorithm>
using namespace std;
#define MAX 11000000
int a[MAX];
int dichotomy(int num,int n) //二分搜索,a[]数组有n个元素返回num的下标
{
int left,middle,right;
left=0;right=n-1;
while(left<=right)
{
middle=(left+right)/2;
if(a[middle]==num) return middle;
else if(num>a[middle]) left=middle+1;
else right=middle-1;
}
return -1; //不存在返回 -1
}
int main()
{
int pd,x,n,i,j,k,itemp,jtemp,temp,t;
while(scanf("%d%d",&x,&n)!=EOF)
{
k=0;
x*=10000000;
for(i=0;i<n;i++)
scanf("%d",&a[i]);
sort(a,a+n);
if(a[n-1]+a[n-2]<x) //剪枝1
{
printf("danger\n");
continue;
}
if(a[0]+a[n]>x&&(2*a[((n-1)/2)+1])<x) //剪枝2
k=((n-1)/2)+1;
for(i=n-1,pd=0;i>=k;i--)
{
temp=x-a[i];
j=dichotomy(temp,i+1); //剪枝3
if(j!=-1&&i!=j)
{
pd=1;
itemp=i;
jtemp=j;
break;
}
}
if(pd)
{
if(itemp<jtemp)
printf("yes %d %d\n",a[itemp],a[jtemp]);
else
printf("yes %d %d\n",a[jtemp],a[itemp]);
}
else
printf("danger\n");
}
return 0;
}
注:原创文章,转载请注明出处