和这道题死刚了一下午…
对二分的认识加深了不少 但我还是好菜啊啊啊啊啊
以这道题的数据范围暴力肯定过不了,下面两道题都是O(nlogn)可以过的。
基本的思路都是找到比分数大的最小的数,然后把它和前一个数(即比分数小的最大的数)与目标分数相减做比较即可。
1、先说偷懒的STL大法
用upper_bound函数找到比目标大的最小的数,返回其所在的位置,对比所有学校分数都低的数特判一下即可
#include
#include
#include
#include
#define MAXN 100005
using namespace std;
int n, m, stu, loc;
int a[MAXN];
long long ans;
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
sort(a + 1, a + n + 1);
for(int i = 1; i <= m; i ++)
{
scanf("%d", &stu);
loc = lower_bound(a + 1, a + n + 1, stu) - a;
if(loc == 1) ans += abs(a[loc] - stu);
else ans += min(abs(a[loc] - stu), abs(a[loc - 1] - stu));
}
printf("%lld\n", ans);
return 0;
}
2、二分(这里才是干货)
之前对到底是 l<=r 还是 l
查找范围为[l, r) r是不包括的
故初始化时为l = 1; r = n + 1;
最终的查找结果为判断条件的最近的一个
这么说可能有点抽象,举个例子为
if(a[mid] >= x) r= mid;
else l = mid + 1;
最终会为l = r >= x 即比x大的最近的数字
这种二分的感觉像是以符合题意与不符合的分界线为目标,不断地往分界线逼近。若分界线比中间小,搜索范围就缩小到头到中间。
按照这个思路的AC代码:
#include
#include
#include
#include
#define MAXN 100005
using namespace std;
int n, m, stu, loc;
int a[MAXN];
long long ans;
void findloc()
{
int l, r, mid;
l = 1;
r = n + 1;
while(l < r)
{
mid = (l + r) >> 1;
if(a[mid] >= stu) r = mid;
else l = mid + 1;
//printf("l is %d, r is %d\n", l, r);
}
loc = l;
return ;
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
sort(a + 1, a + n + 1);
for(int i = 1; i <= m; i ++)
{
loc = 0;
scanf("%d", &stu);
//printf("when is %d time : \n", i);
findloc();
if(loc == 1) ans += abs(a[loc] - stu);
else ans += min(abs(a[loc] - stu), abs(a[loc - 1] - stu));
}
printf("%lld\n", ans);
return 0;
}
b)判断标准:l <= r (最终l会比r大)
查找范围为[l, r]
故初始化时为l = 1; r = n;
举个例子为
if(a[mid] >= x) ans = mid, l = mid + 1;
else r = mid - 1;
最终会为ans >= x 即比x大的最近的数字
这种二分的感觉像是“背道而驰”,为什么这么说呢,如果我们把查找的范围按照题意分为符合和不符合的两部分,符合在左,不符合在右,划个简单的示意图是:
如果查找的mid符合题意,但不一定是最好的,那可能的最优解就在[mid, r]上,把mid存进答案里,之后继续查找[mid+1, r],往那个含有不符合题意的范围去找,原本全部都是符合题意的范围就不管了。
如果如果查找的mid不符合题意,那可能的最优解就在[l , mid - 1]上,而答案也不需要存
这个时候可能存在ans没有被操作的情况,一定要注意初始化!
代码
#include
#include
#include
#include
#define MAXN 100005
using namespace std;
int n, m, stu, loc;
int a[MAXN];
long long ans;
void findloc()
{
int l, r, mid;
l = 1;
r = n;
while(l <= r)
{
mid = (l + r) >> 1;
if(a[mid] <= stu) loc = mid, l = mid + 1;
else r = mid - 1;
//printf("l is %d, r is %d\n", l, r);
}
return ;
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
sort(a + 1, a + n + 1);
for(int i = 1; i <= m; i ++)
{
loc = 0;
scanf("%d", &stu);
//printf("when is %d time : \n", i);
findloc();
//if(loc == 0) ans += abs(a[loc] - stu);
//else
//printf("loc is %d\n", loc);
ans += min(abs(a[loc] - stu), abs(a[loc + 1] - stu));
}
printf("%lld\n", ans);
return 0;
}
但是这个只得了70分。。。我找了半天也没找到bug
所以算了就这样吧
如果有找到bug的大佬欢迎私信我!
我太太太太弱了