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;
}