zoukankan      html  css  js  c++  java
  • 聪明的质检员

    聪明的质检员clever.pas

    【问题描述】

    小 T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有n个矿石,从1到n逐一编号,每个矿石都有自己的重量wi以及价值vi。检验矿产的流程是:

    1、给定m个区间[Li,Ri];

    2、选出一个参数W;

    3、对于一个区间[Li,Ri],计算矿石在这个区间上的检验值Yi:

    Yi = ∑1*∑vj,j∈[Li, Ri]且wj ≥ W,j是矿石编号

    这批矿产的检验结果Y 为各个区间的检验值之和。即:Y = ∑Yi,i ∈[1, m]

    若这批矿产的检验结果与所给标准值S相差太多,就需要再去检验另一批矿产。小T不想费时间去检验另一批矿产,所以他想通过调整参数W的值,让检验结果尽可能的靠近标准值S,即使得S-Y的绝对值最小。请你帮忙求出这个最小值。

    【输入格式】clever.in

    第一行包含三个整数n,m,S,分别表示矿石的个数、区间的个数和标准值。

    接下来的n行,每行2个整数,中间用空格隔开,第i+1行表示i号矿石的重量wi和价值vi 。

    接下来的m行,表示区间,每行2个整数,中间用空格隔开,第i+n+1行表示区间[Li,Ri]的两个端点Li和Ri。注意:不同区间可能重合或相互重叠。

    【输出格式】clever.out

    输出只有一行,包含一个整数,表示所求的最小值。

    【样例输入】

    5 3 15

    1 5

    2 5

    3 5

    4 5

    5 5

    1 5

    2 4

    3 3

    【样例输出】

    10

    【样例说明】

    样例说明:当W选4的时候,三个区间上检验值分别为20、5、0,这批矿产的检验结果为25,此时与标准值S相差最小为10。

    对于10%的数据,有1 ≤ n,m ≤ 10;

    对于30%的数据,有1 ≤ n,m ≤ 500;

    对于50%的数据,有1 ≤ n,m ≤ 5,000;

    对于70%的数据,有1 ≤ n,m ≤ 10,000;

    对于100%的数据,有1 ≤ n,m ≤ 200,000,0 < wi, vi ≤ 10^6,0 < S ≤ 10^12,1 ≤ Li ≤ Ri ≤ n。

    【问题分析】

    首先我们来考虑一下这个W的值的范围,很显然当W的值取为min(wi)时,不管是哪个区域所有的矿石都会被检测,w的值再小于这个值效果也是这样的,所以我们可以确定wL的值为min(wi);而当W的值取max(wi)+1时,所有的矿石都不能被检测,W的值再大于这个值效果也是这样,所以我们可以确定wR的值为max(wi) + 1;

    有了W的这个取值范围后,可们可以枚举所有W的可能,计算出每种可能的检验值之和,找到其与标准值S最小差值的那个。但这个枚举量实在太大,所以这种方法可能但不可行。我们再来看看W取值变化时检测值Y的变化情况:

    很显然当W的值取wL时,此时所有的矿石都会被检验,所以得到的Y值应该是最大,而随着W取值的增大,一部分矿石将不会被检验到,所以Y的值会不断地减小,当W取wR时,此时所有的矿石都不会被检验,所以此时Y=0为最小。这样我们就可以得到一个结论:当wX < wY时,则Y(wX) >=Y(wY)。根据这个变化规则,我们就可以二分获得W的值,从而来计算相应Y的值,若Y的值小于标准差,则W的值减小,即wR = W – 1;否则wL = W + 1。

    这部分的代码可以写成:

    Begin

      readln(n, m, s);

      readln(d[1, 1], d[1, 2]);

      wL := d[1, 1]; wR := d[1, 1];

      For I := 2 to n do begin

        readln(d[I, 1], d[I, 2]);

        if wL > d[I, 1] then wL := d[I, 1];

        if wR < d[I, 1] then wR := d[I, 1];

       end;

      while wL <= wR do begin

        W := (wL + wR) shr 1;

        T := check(W);

        If T < s then wR := W – 1

        Else if T > s then wL := W + 1;

        If min > abs(T – s) then min := T-s;

        If min = 0 then break;

      end;

      writeln(min);

    End.

     

    而对于检验函数check,我们来看看它一般过过程:

    Function check(x : longint) : int64;

    Var

      Y, T, v : int64;

      I, j : longint;

    Begin

      Y := 0;

      For I := 1 to m do begin

        For j := L[i] to R[i] do

          if a[j, 1] >= x then begin

            inc(t); inc(v, a[j, 2]);

          End;

        Inc(Y, t * v);

      End;

      Check := Y;

    End;

     

    由于这部分是双重循环完成,每个区域的1 ≤ Li ≤ Ri ≤ n,所以时间复杂度约为O(mn),显然会超时的。

    需要对这部分代码进行优化处理:由题我们知道,区间[Li, Ri]是连续的一段,若我们能预先计算出从1到i(i<=n)所有满足条件(即矿石的重量大于等于W)的价值和和数量和,那只需要用O(1)的算法即可计算每个区间的价值和和数量和了。

    令t[i]表示1到i满足条件的矿石个数;V[i]表示1到i满足条件的矿石价值和。则有区间[Li, Ri]的检验值为:(t[Ri] – t[Li – 1]) * (V[Ri] – V[Li – 1])。这样把上面的check函数可以优化成:

    Function check(x : longint) : int64;

    Var

      Y: int64;

      V, T : array[0..maxN] of int64;

      I, j : longint;

    Begin

      Fillchar(v, sizeof(v), 0);

      Fillchar(t, sizeof(t), 0);

      //预处理

      For I := 1 to n do begin

        V[i] := v[I – 1];

        T[i] := t[I – 1];

        if a[I, 1] >= x then begin

          inc(v[i], a[I, 2]); inc(t[i]);

        end;

        Y := 0;

        For I := 1 to m do

          Inc(Y, (t[R[i]] – t[L[i] – 1]) * (V[R[i]] – V[L[i] – 1]));

      end;

      Check := Y;

    End;

  • 相关阅读:
    利用DTrace实时检测MySQl
    改进MySQL Order By Rand()的低效率
    RDS for MySQL查询缓存 (Query Cache) 的设置和使用
    RDS For MySQL 字符集相关说明
    RDS for MySQL 通过 mysqlbinlog 查看 binlog 乱码
    RDS for MySQL Mysqldump 常见问题和处理
    RDS for MySQL Online DDL 使用
    RDS MySQL 表上 Metadata lock 的产生和处理
    RDS for MySQL 如何使用 Percona Toolkit
    北京已成为投融资诈骗重灾区:存好骗子公司黑名单,谨防上当!
  • 原文地址:https://www.cnblogs.com/ahmasoi/p/3472148.html
Copyright © 2011-2022 走看看