https://www.luogu.org/problemnew/show/P3959
模拟退火解法
发现prim求最小生成树是明显错误的
因为prim每次要取出边权最小的点
然而在这道T中这样做不一定是最优的
因此可以随机取出点,这就可能是最优
当重复次数达到一定次数时,就极有可能成为最优解
#include <iostream> #include <cstdio> #include <algorithm> #include <queue> using namespace std; const int N = 20; const int oo = 1e8; int n, m; int dis[N][N], deep[N]; struct Node {int u, v;}; Node have_used[N * 100]; bool vis[N]; bool operator < (const Node a, const Node b) { return deep[a.u] * dis[a.u][a.v] > deep[b.u] * dis[b.u][b.v]; } priority_queue <Node> Q; #define gc getchar() inline int read() { int x = 0; char c = gc; while(c < '0' || c > '9') c = gc; while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = gc; return x; } inline int Work(int S) { for(int i = 1; i <= n; i ++) deep[i] = 0, vis[i] = 0; int last(0), ret(0); while(!Q.empty()) Q.pop(); Node E; deep[S] = 1; vis[S] = 1; for(int i = 1; i <= n; i ++) if(dis[S][i] != oo) {E.u = S, E.v = i; Q.push(E);} int T = n - 1; for(; T; T --) { E = Q.top(); Q.pop(); while(!Q.empty() && (vis[E.v] || rand() % n == 0)) { if(!vis[E.v]) have_used[++ last] = E; E = Q.top(); Q.pop(); } vis[E.v] = 1; deep[E.v] = deep[E.u] + 1; for(; last; last --) Q.push(have_used[last]); last = 0; ret += deep[E.u] * dis[E.u][E.v]; Node E2; for(int i = 1; i <= n; i ++) { if((dis[E.v][i] != oo) && !vis[i]) {E2.v = i; E2.u = E.v;Q.push(E2);} } } return ret; } int main() { srand(20001206); n = read(); m = read(); for(int i = 1; i <= n; i ++) for(int j = 1; j <= n; j ++) dis[i][j] = oo; for(int i = 1; i <= m; i ++) { int a = read(), b = read(), c = read(); dis[a][b] = dis[b][a] = min(dis[a][b], c); } int Answer = oo, T = 1000; for(; T; T --) for(int i = 1; i <= n; i ++) Answer = min(Answer, Work(i)); cout << Answer; return 0; }