[NOIP2017 提高組] 寶藏
阿新 • • 發佈:2021-08-04
考慮到這種對於某種操作順序有一個權值。
且這個權值有一個\(O(n)\)或者更好的複雜度求出。
求最值。
那可以用模擬退火。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<ctime> #include<cmath> #define ll long long #define N 20 ll n,m; ll f[N][N]; ll in[N],dis[N]; inline ll find(){ ll ans = 0; for(int i = 1;i <= n;++i) dis[i] = 0; for(int i = 2;i <= n;++i){ ll lim = 1e18; for(int j = 1;j <= i - 1;++j){ if(lim > ((dis[in[j]] + 1) * f[in[j]][in[i]])) lim = ((dis[in[j]] + 1) * f[in[j]][in[i]]),dis[in[i]] = dis[in[j]] + 1; } ans = ans + lim; } return ans; } ll fans = 1e18; inline void sa(){ double T = 20000; double eps = 1e-15; while(T > eps){ ll z = -find(); int x,y; x = rand() % n + 1; y = rand() % n + 1; fans = std::min(fans,-z); std::swap(in[x],in[y]); z = z + find(); if(z > 0 && exp(-z / T) * RAND_MAX < rand()) std::swap(in[x],in[y]); T *= 0.996; } } int main(){ scanf("%lld%lld",&n,&m); for(int i = 1;i <= N;++i) for(int j = 1;j <= N;++j) f[i][j] = 1e18; for(int i = 1;i <= m;++i){ ll x,y,z; scanf("%lld%lld%lld",&x,&y,&z); f[x][y] = std::min(z,f[x][y]); f[y][x] = std::min(z,f[y][x]); } for(int i = 1;i <= n;++i) in[i] = i; fans = find(); while(((double)(clock())/CLOCKS_PER_SEC)<0.5) sa(); std::cout<<fans<<std::endl; }