題目相關
題目鏈接
洛谷,https://www.luogu.com.cn/problem/P1678。
我的OJ,http://47.110.135.197/problem.php?id=4276。
題目描述
現有 m 所學校,每所學校預計分數線是 ai。有 n 位學生,估分分別爲 bi。
根據 n 位學生的估分情況,分別給每位學生推薦一所學校,要求學校的預計分數線和學生的估分相差最小(可高可低,畢竟是估分嘛),這個最小值爲不滿意度。求所有學生不滿意度和的最小值。
輸入格式
第一行讀入兩個整數 m,n。m 表示學校數,n 表示學生數。
第二行有 m 個數,表示 m 個學校的預計錄取分數。
第三行有 n 個數,表示 n 個學生的估分成績。
輸出格式
一行,爲最小的不滿度之和。
輸入樣例
4 3
513 598 567 689
500 600 550
輸出樣例
32
數據規模
m ≤ 100000
ai ≤ 10^6
n ≤ 100000
bi ≤ 10^6
題目分析
題意分析
不需要分析,跳過。
樣例數據分析
根據輸入的樣例數據,我們知道一共有 4 所學校,學校的錄取分數分別爲:513、598、567、689。一共有 3 名考生,高考的分數爲:500、600、550。
第一個考生的分數爲 500,這個分數在所有高校錄取分數 513 以下,因此該學生的不滿意度爲 513-500=13。
第二個考生的分數爲 600,這個分數在所有高校錄取分數 598 和 689 之間,因此該學生的不滿意度有兩個,分別爲 600-598=2 和 689-600=89,根據題意,我們取最小值。因此該學生的不滿意度爲 600-598=2。
第三個考生的分數爲 550,這個分數在所有高校錄取分數 513 和 567 之間,因此該學生的不滿意度有兩個,分別爲 550-513=37 和 567-550=17,根據題意,我們取最小值。因此該學生的不滿意度爲 567-550=17。
這三個學生的所有不滿度合計爲:13+2+17=32。
數據規模分析
ai ≤ 10^6,bi ≤ 10^6。因此對應的分數我們使用 int 可以描述。
m ≤ 100000,n ≤ 100000。這樣極限數據應該是所有高校錄取分數都是 10^6,而考生分數是 0 分。這樣對應的最大值爲 10^6*10^6=10^12,超過了 int 的範圍。所以答案需要用 long long 來描述。
算法思路
從樣例數據分析中,我們可以看出,本題的核心就是根據每個學生的分數,找出比這個分數小的錄取分數的最大值和比這個分數大的錄取分數的最小值。既然是查找問題,我們肯定可以使用二分查找來實現,二分查找的時間複雜度爲 O(logn)。最多的數據爲 1e5 個,因此可以滿足。
1、讀入數據。
2、對高校錄取分數進行排序。
3、應該是沒有必要進行出重,當然去重也是可以的。
4、對每個學生高考分數,在錄取分數中查找左下界和右上界。
5、如果左下界不等於右上界,說明高考分數和某個學校的錄取分數相同。也就意味着這個學生的不滿度爲 0。
6、如果左下界等於右上界,說明學生高考分數在兩個高校錄取分數之間。需要分類討論。分佈右三種情況:一是小於高校錄取分數最小值,這個時候 lo=hi=0,那麼不滿值應該是(高校錄取分數最小值 - 高考分數);二是大於高校錄取分數最大值,這個時候 lo=hi=m,那麼不滿值應該是(高考分數 - 高校錄取分數最大值);三是在兩個高校錄取分數線之間,那麼不滿足應該右有兩個,即(高考分數 - 高校錄取分數[lo-1])和(高校錄取分數[hi] - 高考分數),我們取最小值即可。
AC 參考代碼
根據上面的分析,我們知道核心就是找出考生分數在高校錄取分數的左下界(hi)和右上界(lo)。
/*
OJ:luogu
題目:P1678 煩惱的高考志願
地址:https://www.luogu.com.cn/problem/P1678
*/
#include <bits/stdc++.h>
const int MAXM = 1e5+2;
int a[MAXM];//學校分數
const int MAXN = 1e5+2;
int b[MAXM];//學生分數
int main() {
int n, m;
scanf("%d%d", &m, &n);
int i;
//讀入學校分數
for (i=0; i<m; i++) {
scanf("%d", &a[i]);
}
//讀入學生分數
for (i=0; i<n; i++) {
scanf("%d", &b[i]);
}
std::sort(a, a+m);//排序
//統計
unsigned long long ans = 0;
for (i=0; i<n; i++) {
int lo = std::lower_bound(a, a+m, b[i]) - a;
int hi = std::upper_bound(a, a+m, b[i]) - a;
if (lo==hi) {
//b[i]沒有在a[i]中,找最小的
if (0==lo) {
ans += (a[lo]-b[i]);
} else if (hi>=m) {
ans += (b[i]-a[hi-1]);
} else {
ans += std::min(b[i]-a[lo-1], a[hi]-b[i]);
}
}
}
printf("%llu\n", ans);
return 0;
}
代碼細節
就是對 hi 和 lo 的值進行分類討論。