題解 P2538 【[SCOI2008]城堡】
阿新 • • 發佈:2020-07-17
link
solve
被模擬退火的標籤吸引來了,沒想到是一道黑題,就被我用模擬退火水過去了。
我們可以把沒有城堡的城市看成是一個序列,序列的前k項我們可以用來做城堡,我可以先用Floyd預處理出每個點之間的距離,然後用\(n^2\)的時間統計出此時的\(max(dis x)\)所以模擬退火每次交換序列中的兩個元素就好了。
code
#include<iostream> #include<cstdio> #include<stdlib.h> #include<cmath> #include<ctime> #define rep(i,a,b) for(register int i=a;i<=b;i++) #define maxn 500 #define inf 0x3f3f3f3f #define delta 0.996 using namespace std; inline int read() { int f=1,x=0; char ch=getchar(); while(ch<'0'||ch>'9') {if(ch=='-') f=-f;ch=getchar();} while('0'<=ch&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return x*f; } void write(int x) { if(x<0) x=-x,putchar('-'); if(x>9) write(x/10); putchar(x%10+'0'); } struct edge{ int a,b,next,v; }e[maxn]; int head[maxn],cnt,n,m,k,v[maxn],cas[maxn]; int dis[maxn][maxn],p[maxn],N,X[maxn],ans=1e8; double t; int calculate(int x[]) { rep(i,1,k) cas[x[i]]=1; int res=-inf; rep(i,0,n) { int minn=inf; rep(j,0,n) if(cas[j]) minn=min(minn,dis[i][j]); res=max(res,minn); } rep(i,1,k) cas[x[i]]=0; return res; } void simulate_anneal() { int a[maxn]; rep(i,1,N) a[i]=p[i]; t=5000; while(t>1e-15) { int b[maxn]; rep(i,1,N) b[i]=a[i]; int x=rand()%N+1; int y=rand()%N+1; swap(b[x],b[y]); int now=calculate(b); double Delta=now-ans; if(Delta<0) { ans=now; rep(i,1,N) p[i]=a[i]=b[i]; } else if(exp(-Delta/t)*RAND_MAX>rand()) { rep(i,1,N) a[i]=b[i]; } t*=delta; } } void work() { while((double)clock()/CLOCKS_PER_SEC<=0.6)simulate_anneal(); } int main() { freopen(".in","r",stdin); freopen(".out","w",stdout); srand(time(0)); n=read(),m=read(),k=read(); n--; rep(i,0,n) X[i]=read(); rep(i,0,n) v[i]=read(); rep(i,1,m) { int a=read(); cas[a]=1; } rep(i,0,n) if(!cas[i]) p[++N]=i; rep(i,0,n) rep(j,0,n) dis[i][j]=inf; rep(i,0,n) dis[i][X[i]]=dis[X[i]][i]=min(v[i],dis[i][X[i]]),dis[i][i]=0; rep(c,0,n) rep(i,0,n) rep(j,0,n) dis[i][j]=min(dis[i][j],dis[i][c]+dis[c][j]); work(); write(ans); return 0; }