解题报告 『宝藏(Prim思想 + 访问顺序随机)』

原题地址

本以为不过是一道Prim算法模版题,但貌似只能得45分,虽然对我这种蒟蒻来说已经够了

然而同机房大佬表示可以用模拟退火A了此题,遂习之,终无所获

然而机缘巧合之下习得了另一种随机算法,于是搭配Prim算法,竟然过了!

 

首先我们要明确一点:单纯的Prim算法为什么不行。相信聪明的你已经知道了,比如下面就是一个反例:

4 4

1 2 2

1 3 20

2 3 3

3 4 500

Prim算法得出的答案为1508,但是最优解应为507,成功hack。

 

那么Prim算法是否就真的没有用武之地了呢?非也,我们可以抽出Prim算法的思想——每次取最小。

 

随机化部分

比如上面那张图,我们按照Prim算法的思想来跑的话,访问顺序如下:

1 => 2 => 3 => 4

但如果访问顺序恰好为:

  3 => 4 => 2 => 1 

 便可以得到最优解507。

 

基于此思想,我们可以随机访问顺序,然后按照这个访问顺序跑贪心,或许这样不能过,但在大量的随机下,我们十有八九会得出正确的答案,而我们按照一个顺序求出答案仅仅只是n^2的(更何况n最大只有12),所以……

已经没有什么好害怕的了

 

代码实现如下:

#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (register int i = (a); i <= (b); i++)

const long long maxn = 12 + 5, T = 5e3, INF = 1e9;

long long n, m, ans = INF;
long long a[maxn], dis[maxn], edge[maxn][maxn];

long long read() {
    long long x = 0, flag = 0;
    char ch = ' ';
    while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
    if (ch == '-') {
        flag = 1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 1) + (x << 3) + ch - '0';
        ch = getchar();
    }
    return flag ? -x : x;
}

void origin() {
    rep(i, 1, n)
        rep(j, 1, n) 
            edge[i][j] = ((i == j) ? 0 : INF); 
    rep(i, 1, n) a[i] = i;//最开始的访问顺序就按照1,2,3,4,...,n.
}

long long check() {
    memset(dis, 0, sizeof(dis));
    long long cost = 0, start = a[1];//取出第一个点.
    dis[start] = 0;
    rep(i, 2, n) {
        int v = a[i];
        int cost_v = INF;
        rep(j, 1, i - 1) {
            int u = a[j];
            if (edge[u][v] * (dis[u] + 1) < cost_v) {
                cost_v = edge[u][v] * (dis[u] + 1);
                dis[v] = dis[u] + 1;
            }
        }
        if (cost_v >= INF) return INF;
        cost += cost_v;
    }
    return cost;
}

void work() {
    long long tot = T;
    while (tot--) {
        long long x = rand() % n + 1, y = rand() % n + 1;
        swap(a[x], a[y]);//随机交换两个数以达成随机序列.
        long long new_ans = check();
        ans = min(ans, new_ans); 
    }
}

void write(int x) {
    if (x < 0) {
        putchar('-');
        x = -x;
    }
    if (x > 9) write(x / 10);
    putchar(x % 10 + '0');
}

int main() {
    n = read(), m = read();
    origin();
    rep(i, 1, m) {
        long long u, v, w;
        u = read(), v = read(), w = read();
        edge[u][v] = min(edge[u][v], w);
        edge[v][u] = min(edge[v][u], w);
    }
    work(); 
    write(ans);
    return 0;
}
View Code
posted @ 2019-01-21 20:56  雲裏霧裏沙  阅读(295)  评论(0编辑  收藏  举报