• [CQOI2012]组装 贪心


    [CQOI2012]组装

    LG传送门

    首先有一个必须要能推的式子:设第(i)种零件选的生产车间位置为(x _ i),组装车间位置为(x), 则总的花费为

    [f(x) = sum limits _{i = 1} ^ n (x - x_i) ^ 2 ]

    [= n x^ 2 - 2 sum limits _{i = 1} ^ n x _ i x + sum limits _{i = 1} ^ n x _ i ^ 2 ]

    这是一个关于(x)的二次函数, 在(x = frac {sum limits _{i = 1} ^n x_i}{n})时取得最小值(sum limits _{i = 1} ^ n x _ i ^ 2 - frac {sum limits _{i = 1} ^ n x _ i} {n})。做到这步,我们就可以获得前40分,只需要枚举每种零件选的生产车间,复杂度是指数级的。考虑贪心优化枚举的过程。

    下面为了表述方便,设(o = sum limits _ {i = 1} ^ n x _ i ^ 2)(e = sum limits _{i = 1} ^ n x _ i)

    首先我们对于同一种零件的生产车间按坐标从小到大排序,每次枚举把某种零件的生产车间替换成他的下一个,这样是有一些情况枚举不到的,但事实上我们只要保证可能的情况都枚举到了就行了,于是贪心.

    先给出贪心的结论:设一次替换用一个二元组({x _ i, y _ i}(x_i < y_i))来表示,如果我们先把表示替换的二元组按照(x _ i + y _ i)从小到大排序,这样就一定不会错过最优解。

    下面证明这个结论:用反证法,假设我们这样做会错过最优解,那么一定存在({x_1, y_1})({x_2, y_2})表示的替换使得(x_1)(y_2)是满足最优解的条件,且替换({x_1, y_1})比替换({x_2, y_2})先进行。由于(x_1)是满足最优解的条件,而(y_1)不是,那么必有(frac {e} {n})(满足最优解的组装车间)(< frac {x_1+ y_1} {2})(二者之间线段的中点),这个结论是显然的(可以自己手玩)。同理有(frac {e} {n} > frac {x_2 + y_2} {2}),由此及上式得(x_1 + y_1 > x_2 + y_2),但我们已经事先排序保证(x_1 + y_1 < x_2 + y_2),矛盾。

    实现起来就非常简单了,每次替换维护(o)(e)的变化量,再用(o - frac {e ^ 2} {n})(e / n)更新最小值和答案就行了。

    #include <cstdio>
    #include <cctype>
    #include <vector>
    #include <algorithm>
    #define R register
    #define I inline
    #define B 1000000
    #define D double
    #define P pair <int, int>
    using namespace std;
    const int N = 10003;
    char buf[B], *p1, *p2;
    I char gc() { return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, B, stdin),p1 == p2) ? EOF : *p1++; }
    I int rd() {
        R int f = 0, b = 1;
        R char c = gc();
        while ((c < 48 || c > 57) && c ^ 45)
            c = gc();
        if (c == 45)
            b = 0, c = gc();
        while (c > 47 && c < 58)
            f = f * 10 +(c ^ 48), c = gc();
        return b ? f : ~f + 1;
    }
    vector <int> f[N];
    vector <pair <int, int> > g;
    I D pow(D x) { return x * x; }
    I int cmp(P x, P y) { return x.first + x.second < y.first + y.second; }
    int main() {
        R int n = rd(), m = rd(), i, j, s, x, y;
        D o = 0, e = 0, del, tmp, ans;
        for (i = 1; i <= m; ++i)
            x = rd(), y = rd(), f[y].push_back(x);
        for (i = 1; i <= n; ++i) {
            s = f[i].size(), sort(&f[i][0], &f[i][0] + s);
            for (j = 1; j < s; ++j)
                g.push_back(make_pair(f[i][j - 1], f[i][j]));
        }
        for (i = 1; i <= n; ++i)
            o += pow(f[i][0]), e += f[i][0];
        tmp = o - pow(e) / n, ans = e / n, s = g.size(), sort(&g[0], &g[0] + s, cmp);
        for (i = 0; i < s; ++i) {
            o += pow(g[i].second) - pow(g[i].first), e += g[i].second - g[i].first;
            if ((del = o - pow(e) / n) < tmp)
                tmp = del, ans = e / n;
        }
        printf("%.4lf", ans);
        return 0;
    }
    
    
  • 相关阅读:
    Android数据存储之File
    Openfiler使用说明
    nginx的upstream目前支持5种方式的分配
    centOS 6.5关闭防火墙步骤
    yum下载文件的存放位置
    ubuntu如何开启root,如何启用Ubuntu中root帐号
    ubuntu下的apt-get内网本地源的搭建
    [转]StarWind模拟iSCSI设备
    ORACLE删除某用户下所有对象
    [转]oracle导入提示“IMP-00010:不是有效的导出文件,头部验证失败”的解决方案
  • 原文地址:https://www.cnblogs.com/cj-chd/p/10342289.html
Copyright © 2020-2023  润新知