题目链接: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; }