2018年9月3日 貝殼筆試題(線段樹+二分查詢)
阿新 • • 發佈:2019-01-07
題目:
演算法思路:
考慮每一個骨牌:倒下後產生區間。 區間內會連帶後面未知長度區間的骨牌倒下。假設最後倒下的區間為。
首先很容易寫出時間複雜度為(肯定超時)的動規方程:其中為滿足的最大值;表示第i個骨牌倒下後,後面所有連帶倒下骨牌的最右端的x座標。優化該方程求最大值部分需要引入線段樹優化時間複雜度。線段樹節點儲存的值為區間內所有骨牌倒下後,之後接連倒下骨牌的最遠距離(即最大x下標值)。需要說明一點,線段樹初始化流程:葉子節點初始化。之後單點更新葉子值為(這裡的時間複雜度為,時間妥妥夠)。這裡查詢葉子k使用二分查詢,搜尋為不大於的最小值;query為區間內連帶倒下骨牌的最遠距離。注意這裡中的i應從大到小更新。
整棵線段樹更新完畢後即可查詢區間最遠連帶倒下骨牌的下標 。然後有了 即可用二分查詢,搜尋區間內有多少元素即可。
注:該程式碼未經驗證正確性。如有疑問請傳送郵件[email protected]討論。或有更簡單方法歡迎致信。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define INF 1e7
#define N 100010
#define M 100010
struct Node {
int x, h;
int id, ans;
}nodes[N];
bool comp1(Node a, Node b) {
return a.x < b.x;
//<升序,>降序,a.x以x排序
}
bool comp2(Node a, Node b) {
return a.id < b.id;
//<升序,>降序,a.id以a排序
}
int max(int a, int b){
return a>b?a:b;
}
int binary_search(int l, int r, int value){
//printf("%d,%d\n",l,r);
if(l==r) return l;
int mid = ( l + r ) / 2;
if( value <= nodes[mid].x )
return binary_search(l, mid, value);
return binary_search(mid+1, r, value);
}
int tree[4 * N];
void update(int n) {
tree[n] = max( tree[n << 1], tree[n << 1 | 1] );
}
void build_tree(int left, int right, int n) {
if (left == right) {
//cin >> tree[left];
tree[n] = nodes[left].x + nodes[left].h;
return;
}
int mid = (left + right) >> 1;
build_tree(left, mid, n << 1);
build_tree(mid + 1, right, n << 1 | 1);
update(n);
}
// 單點更新操作; left一般恆等於1,right一般恆等於n,n一般恆等於1
void add(int a, int b, int left, int right, int n) {
if (left == right) {
tree[n] = max(tree[n], b);
return;
}
int mid = (left + right) >> 1;
if (a <= mid) {
add(a, b, left, mid, n << 1);
} else {
add(a, b, mid + 1, right, n << 1 | 1);
}
update(n);
}
// l為要查詢的區間左邊界; left一般恆等於1,right一般恆等於n,n一般恆等於1
int query(int l, int r, int left, int right, int n) {
if (l <= left && r >= right) return tree[n];
int mid = (left + right) >> 1;
int max_ = max(tree[left],tree[right]);
if (l <= mid) {
max_ = max(max_, query(l, r, left, mid, n << 1));
}
if (r > mid) {
max_ = max(max_, query(l, r, mid + 1, right, n << 1 | 1));
}
return max_;
}
void work(int n){
build_tree(1,n,1);
for(int i = n;i>=1;i--){ //從右向左更新單點
int right_id = binary_search(1,n,nodes[i].x+nodes[i].h-1);
int max_ = query(i,right_id,1,n,1);
add(i,max_,1,n,1);
}
for(int i =1;i<=n;i++){
// 查詢x[k] <= x[i]+h[i]-1的最大k值
int right_id = binary_search(1,n,nodes[i].x+nodes[i].h-1);
// 查詢i到i倒下區間內連帶倒下的骨牌最遠距離
int max_ = query(i,right_id,1,n,1);
// 二分查詢x[i]到該最遠距離之間有多少個元素
int max_right_id = binary_search(1,n,max_);
nodes[i].ans = max_right_id - i + 1;
}
}
int main() {
int n, m;
freopen("../test.txt", "r", stdin);
while( ~scanf("%d", &n) ){
for(int i = 1;i<=n;i++){
scanf("%d%d", &nodes[i].x,&nodes[i].h);
nodes[i].id = i;
}
sort(nodes+1, nodes+n+1, comp1); // 按照x值排序
work(n);
sort(nodes+1, nodes+n+1, comp2); // 按照輸入id值排序
for(int i =1;i<n;i++){
printf("%d ", nodes[i].ans);
}
printf("%d\n", nodes[n].ans);
}
return 0;
}