洛谷 P2503 [HAOI2006]均分数据

洛谷 P2503 [HAOI2006]均分数据_第1张图片

方法一:模拟退火

思路:用DP跑出固定分组的解,与模拟退火模板结合即可。产生新解需要随机交换数组a中的两个数据,可以随机生成两个不同的下标。

#include 
using namespace std;
#define re register//加速
#define sqr(x) ((x)*(x))//简化代码
const double MAX_TIME = 0.8;//可取0.7~0.8
const double T0 = 2000;//初始温度
const double Tk = 1e-6;//终止温度
const double delta = 0.993;//降温系数

int n, m;
int a[30], s[30];
double ans;
double dp[30][30];

double DP() { //DP确定每组多少个数据
    memset(dp, 127, sizeof(dp));
    dp[0][0] = 0;
    for (re int i = 1; i <= n; i++) s[i] = s[i - 1] + a[i];
    for (re int i = 1; i <= n; i++)
        for (re int j = 1; j <= i; j++)
            for (re int k = 0; k < i; k++)
                dp[i][j] = min(dp[i][j], dp[k][j - 1] + 
                sqr(s[i] - s[k] - (double)s[n] / m));
    return dp[n][m];
}
void SA() { //模拟退火
    double t = T0;
    while (t > Tk) {
        // 交换两个数据产生新序列a
        int x = 0, y = 0;
        while (x == y) x = rand() % n + 1, y = rand() % n + 1;//随机产生两个不同的下标交换
        swap(a[x], a[y]);

        double nowAns = DP();
        double deltaAns = nowAns - ans;
        if (deltaAns < 0) ans = nowAns;//nowAns < ans,此解更优,更新ans
        //else if()已一定概率接受新解,已swap,不更新ans
        else if (exp(-deltaAns / t) * RAND_MAX > rand()) x = x;
        else swap(a[x], a[y]);//拒绝新解,换回
        t *= delta;//降温
    }
}

inline void solve() {
    ans = 1e10;
    //卡时,在不会 TLE 的情况下尽量多的跑 SA
    while ((double)clock() / CLOCKS_PER_SEC < MAX_TIME) SA();
}

int main() {
    cin >> n >> m;
    for (re int i = 1; i <= n; i++) cin >> a[i];
    solve();
    if (ans / m < 1e-6) cout << "0.00";
    else printf("%.2f\n", sqrt(ans / m));
    return 0;
}

你可能感兴趣的:(洛谷)