1. 程式人生 > 其它 >洛谷題解:P1314 [NOIP2011 提高組] 聰明的質監員

洛谷題解:P1314 [NOIP2011 提高組] 聰明的質監員

技術標籤:ACM演算法二分法

洛谷題解:P1314 [NOIP2011 提高組] 聰明的質監員

題目:

小T 是一名質量監督員,最近負責檢驗一批礦產的質量。這批礦產共有 n個礦石,從 1 到 n 逐一編號,每個礦石都有自己的重量 wi以及價值 vi 。檢驗礦產的流程是:

1 、給定m個區間 [li,ri]

2 、選出一個引數 W;

3 、對於一個區間 [li,ri],計算礦石在這個區間上的檢驗值 yi :

其中 j 為礦石編號。

這批礦產的檢驗結果 y 為各個區間的檢驗值之和。即:

若這批礦產的檢驗結果與所給標準值 s 相差太多,就需要再去檢驗另一批礦產。小T 不想費時間去檢驗另一批礦產,所以他想通過調整引數 W 的值,讓檢驗結果儘可能的靠近標準值 s,即使得 ∣s−y∣ 最小。請你幫忙求出這個最小值。

輸入格式:

第一行包含三個整數 n,m,分別表示礦石的個數、區間的個數和標準值。

接下來的 n 行,每行兩個整數,中間用空格隔開,第 i+1 行表示 i 號礦石的重量 wi 和價值 vi。

接下來的 m 行,表示區間,每行兩個整數,中間用空格隔開,第 i+n+1行表示區間 [li,ri] 的兩個端點 li 和 ri。注意:不同區間可能重合或相互重疊。

輸出格式:

一個整數,表示所求的最小值。

樣例:

輸入:

5 3 15
1 5
2 5
3 5
4 5
5 5
1 5
2 4
3 3

輸出:

10

分析:

可以看出來,這個函式是隨W的增加遞減的,由於要對W進行查詢遍歷,所以使用二分,找到一個結果最接近s的W。兩個端點的值自然是w陣列的最小值與最大值。

然後check()的就是公式結果的值與s的大小關係,如果ans > s,說明ans還需要再小,函式還是單減的,所以要選擇W更大的區間。

然後對於這個函式值的計算,對於每一個W,都要迴圈m次,每一次迴圈內還要從l[i]r[i]再迴圈進行判斷,這樣執行了TLE了。看來是需要用字首和,這樣就免去了從l[i]r[i]的迴圈的複雜度,但是需要提前對陣列單另判斷一次,不符合要求的不自增。

程式碼如下:

#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
#include<cmath> #include<vector> using namespace std; long long n, m, s; long long w[300005] = {0}; long long v[300005] = {0}; long long l[300005] = {0}; long long r[300005] = {0}; long long pre_w[300005] = {0}; long long pre_v[300005] = {0}; long long mini = 2147483647; long long maxi = -1; long long result = 0; long long C_Y(long long W) { long long ans = 0; memset(pre_v,0,sizeof(pre_v)); memset(pre_w,0,sizeof(pre_w)); for(long long i = 1;i <= n;i++) { if(w[i] >= W) { pre_w[i] = pre_w[i - 1] + 1; pre_v[i] = pre_v[i - 1] + v[i]; } else { pre_w[i] = pre_w[i - 1]; pre_v[i] = pre_v[i - 1]; } } for(long long i = 1;i <= m;i++) { ans += (pre_w[r[i]] - pre_w[l[i] - 1]) * (pre_v[r[i]] - pre_v[l[i] - 1]); } return ans; } bool check(long long ans) { result = llabs(ans - s); if(ans > s) { return true; } else { return false; } } int main() { cin>>n>>m>>s; for(long long i = 1;i <= n;i++) { cin>>w[i]>>v[i]; maxi = max(maxi, w[i]); mini = min(mini, w[i]); } for(long long j = 1;j <= m;j++) { cin>>l[j]>>r[j]; } long long left = mini, right = maxi; long long mid; long long ans = 0x3f3f3f3f3f3f3f3f; while(left <= right) { mid = (right + left) / 2; if(check(C_Y(mid))) { left = mid + 1; } else { right = mid - 1; } if(ans > result) { ans = result; } } cout<<ans<<endl; return 0; }