简单搜索入门(二):二分答案 HDU 5248
来源:互联网 发布:淘宝的皇家及行靠谱吗 编辑:程序博客网 时间:2024/05/17 02:42
二分练习的第二部分——二分答案的查找
1、HDU 5248 序列变换
Description
给定序列A={A1,A2,…,An}, 要求改变序列A中的某些元素,形成一个严格单调的序列B(严格单调的定义为:Bi< Bi+1, 1≤i< N)。
我们定义从序列A到序列B变换的代价为cost(A,B)=max(|Ai−Bi|)(1≤i≤N)。请求出满足条件的最小代价。注意,每个元素在变换前后都是整数。
Input
第一行为测试的组数T(1≤T≤10).
对于每一组:
第一行为序列A的长度N(1≤N≤105),第二行包含N个数,A1,A2,…,An.
序列A中的每个元素的值是正整数且不超过106。
Output
对于每一个测试样例,输出两行:
第一行输出:”Case #i:”。i代表第 i 组测试数据。
第二行输出一个正整数,代表满足条件的最小代价。
Sample Input
2
2
1 10
3
2 5 4
Sample Output
Case #1:
0
Case #2:
1
Solution
一开始阅读时以为是一道贪心的题目,就用贪心的策略敲了一遍,发现一直WA。后来看了一眼数据范围,就感觉像是二分,另一方面,注意到cost是最大值,只需要二分一下需要改变的最大值,然后判断当前的答案能否将原来的数列改变成一个严格单调的序列。
Code
#include <cstdio>#include <cstring>#include <cstdlib>#define MAXN 100003int a[MAXN],n;bool check(int s){ int st=a[1]-s;//第一个数字越小对后来越有利 for(int i=2;i<=n;i++) { int w=a[i]-s;//对当前的数字做最大尺度的改变 if(w<=st) { w=st+1;//可以构造的话就让当前的数字越小越好 if(abs(w-a[i])>s)//如果连最小的需求都无法满足则返回 return false; } st=w; } return true;}int main(){ int k; while(scanf("%d",&k)!=EOF) { for(int j=1;j<=k;j++) { printf("Case #%d:\n",j); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); int l=0,r=1000001;//初始化最大值 while(l<r) { int mid=(l+r)>>1; if(check(mid))//如果当前的值能够成功取右区间 r=mid; else l=mid+1;//如果不能则取左区间 } printf("%d\n",r);//l与r都可以, } } return 0;}
2、POJ 3579 Median
Time Limit: 1000MS
Memory Limit: 65536K
Total Submissions: 8524
Accepted: 2973
Description
Given N numbers, X1, X2, … , XN, let us calculate the difference of every pair of numbers: ∣Xi - Xj∣ (1 ≤ i < j ≤ N). We can get C(N,2) differences through this work, and now your task is to find the median of the differences as quickly as you can!
Note in this problem, the median is defined as the (m/2)-th smallest number if m,the amount of the differences, is even. For example, you have to find the third smallest one in the case of m = 6.
Input
The input consists of several test cases.
In each test case, N will be given in the first line. Then N numbers are given, representing X1, X2, … , XN, ( Xi ≤ 1,000,000,000 3 ≤ N ≤ 1,00,000 )
Output
For each test case, output the median in a separate line.
Sample Input
4
1 3 2 4
3
1 10 2
Sample Output
1
8
Source
POJ Founder Monthly Contest – 2008.04.13, Lei Tao
题意
对于给定的N个数字,将N个数任意两项的差值共M个全部算出,从小到大排序找出其中的中位数。
Solution
经典的二分查找答案的题目,通过两次的二分,第一次查找答案,第二次二分判断当前答案的正确性。
最大的差值不断二分答案每次判断一下当前差值t,用sum记录比a[i]大t的数字的个数,如果sum大于m/2,就取右区间,反之取左区间。
PS:二分查找在STL中有现成的模板
lower_bound(a,a+n,t)在数组中查找第一个大于(或等于)t的数字,返回指向大于(或等于)t的第一个迭代器—返回指针。
upper_bound(a,a+n,t)在数组中查找第一个大于t的数字,返回大于t的迭代器。
Code
#include <cstdio>#include <algorithm>using namespace std;int n;long a[100003],m,l,r,mid,res;bool che (int t){ int sum=0; for(int i=1;i<=n;i++) sum+=n-(lower_bound(a+1,a+n+1,a[i]+t)-a-1); if(sum>m) return true; else return false;}int main (){ while(scanf("%d",&n)!=EOF) { for(int i=1;i<=n;i++) scanf("%ld",&a[i]); sort(a+1,a+1+n);//排序保证二分的正确性 m=n*(n-1)/4; l=0,r=a[n]-a[1];//初始化最大值为最大数字与最小数字的差值 while(l<=r)//二分答案 { mid=(l+r)>>1; if(che(mid)) { res=mid; l=mid+1; } else r=mid-1; } printf("%d\n",res); } return 0;}