• [HAOI2006]均分数据


    题意

    Here

    思考

    今天练习的第二道模拟退火……
    (WA) 了好几次发现是最后没有开根号!!

    参考了一下 (attack) 的题解,主要思路是先随机分组,然后随机选一个数分到权值最小的组里来退火。(ps:玄学质数好用)

    代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef double D;
    const int N = 110;
    int a[N], pos[N], cnt[N], n, m;
    D sum[N], ans = 1e18, x, SUM, ANS = 1e18;
    void SA(){
        memset(sum, 0, sizeof(sum)); ANS = 0;
        for(int i=1; i<=n; i++) pos[i] = rand() % m + 1, sum[pos[i]] += a[i];
        for(int i=1; i<=m; i++) ANS += (sum[i] - x) * (sum[i] - x);
        D t = 10000; int p, i;
        while(t > 1e-14){
            p = min_element(sum + 1, sum + m + 1) - sum;
            i = rand() % n + 1;
            D PANS = ANS;
            ANS -= (sum[ pos[i] ] - x) * (sum[ pos[i] ] - x) + (sum[p] - x) * (sum[p] - x);
            sum[ pos[i] ] -= a[i]; sum[p] += a[i];
            ANS += (sum[ pos[i] ] - x) * (sum[ pos[i] ] - x) + (sum[p] - x) * (sum[p] - x);
            if( (PANS > ANS) || (exp( (D)(ANS - PANS) / t) < ((D)rand() / (D)RAND_MAX)) ){
                pos[i] = p;
            }
            else{
                sum[p] -= a[i]; sum[pos[i]] += a[i]; ANS = PANS;
            }
            t *= 0.99;
        }
        if(ANS < ans) ans = ANS;
    }
    int main(){
        srand(time(0)); srand(19260817); srand(rand());
        cin >> n >> m;
        for(int i=1; i<=n; i++){
            cin >> a[i]; x += a[i];
        }
        x /= m;
        int cnt = 1926;
        while(cnt--){
            SA();
        }
        cout << fixed << setprecision(2) << sqrt(ans / (D)m);
        return 0;
    }
    
    

    总结

    感觉这种随机化算法很玄学,考试的话还是建议先打完暴力分,如果实在没思路再考虑模拟退火,不过算法本身局限性也很大,适用于数据范围小一些的题,部分计算几何也可做。

  • 相关阅读:
    字符串加密和解密的常类
    vs2013中使用nuget下载cefsharp winform包
    序列积第m小元素 二分答案优化
    贪心 park
    struct结构体 重载运算符
    最小生成树kruskal 知识点讲解+模板
    c++快读与快输模板
    MZOJ #82 总统竞选
    MZOJ #83 位运算
    MZOJ #81 最小得分和
  • 原文地址:https://www.cnblogs.com/alecli/p/9917425.html
Copyright © 2020-2023  润新知