1. 程式人生 > >聰明的質檢員

聰明的質檢員

main 不同 相差 監督員 freopen bits spa 算法 時間

【Problem description】

小 T 是一名質量監督員,最近負責檢驗一批礦產的質量。這批礦產共有n 個礦石,從1到n 逐一編號,每個礦石都有自己的重量wi 以及價值vi。檢驗礦產的流程是:
  1、給定m 個區間[Li,Ri];
  2、選出一個參數W;
  3、對於一個區間[Li,Ri],計算礦石在這個區間上的檢驗值Yi :

  這批礦的檢驗結果Y 為各個區間的檢驗值之和。即:
  若這批礦產的檢驗結果與所給標準值S 相差太多,就需要再去檢驗另一批礦產。小T不想費時間去檢驗另一批礦產,所以他想通過調整參數W 的值,讓檢驗結果盡可能的靠近標準值S,即使得S-Y 的絕對值最小。請你幫忙求出這個最小值。

【Input format】

第一行包含三個整數 n,m,S,分別表示礦石的個數、區間的個數和標準值。
  接下來的 n 行,每行2 個整數,中間用空格隔開,第i+1 行表示i 號礦石的重量wi 和價值vi 。
  接下來的 m 行,表示區間,每行2 個整數,中間用空格隔開,第i+n+1 行表示區間[Li,Ri]的兩個端點Li 和Ri。註意:不同區間可能重合或相互重疊。

【Output format】

輸出只有一行,包含一個整數,表示所求的最小值。

【Algorithm design】

二分答案+前綴和

【Problem analysis】

求最接近標準的答案,很明顯二分答案。當然二分Y明顯很困難,應該分治W的值。這裏又可以引入離散化思想,因為W一定是一個礦的質量,不然不會有變化。確定W之後就是怎麽求Y的問題,首先要讀懂題目,其實意思很簡單,就是一個區間內可行礦的數量*可行礦的價值總和,因為區間老是重疊,如果循環處理,最大可能達到O(nm),所以想到前綴和處理,紀錄1到i的可行礦數量和價值總和(一開始還想到線段樹..),最後就是O((n+m)logn)的算法

【Source code】

#include <bits/stdc++.h>

#define MAXN 200010

#define ll long long

using namespace std;

int n,m;

ll s,ans,sumx[MAXN];

int w[MAXN],v[MAXN],b[MAXN],e[MAXN],sortw[MAXN],sum[MAXN];

void search(int l,int r)

{

if(l==r-1)

return ;

int mid=(l+r)>>1;

ll y=0;

for(int i=1;i<=n;i++)

{

sum[i]=sum[i-1];

sumx[i]=sumx[i-1];

if(w[i]>=sortw[mid])

sum[i]+=1,sumx[i]+=v[i];

}//在W確定後處理出前綴和

for(int i=1;i<=m;i++)

y+=(sum[e[i]]-sum[b[i]-1])*(sumx[e[i]]-sumx[b[i]-1]);//利用前綴和處理出Y

ans=min(ans,abs(y-s));//比較後紀錄最小差值

if(y<s)

search(l,mid);

else

search(mid,r);

}

int main()

{

freopen("qc.in","r",stdin);

freopen("qc.ans","w",stdout);

cin>>n>>m>>s;

for(int i=1;i<=n;i++)

cin>>w[i]>>v[i],sortw[i]=w[i];

for(int i=1;i<=m;i++)

cin>>b[i]>>e[i];

sort(sortw+1,sortw+n+1);//將礦值排序以便於二分

ans=1000000000001;

search(0,n+1);

cout<<ans<<endl;

return 0;

}

聰明的質檢員