洛谷:P2538 [SCOI2008]城堡


洛谷:P2538 [SCOI2008]城堡

题目传送门

模拟退火:

定义:模拟退火算法是一种随机化算法。其原型来源于固体的退火过程。将固体加温至充分高,再让其徐徐冷却,加温时,固体内部粒子温升变为无序状,内能增大,而徐徐冷却时粒子渐趋有序,在每个温度都达到平衡态,最后在常温时达到基态,内能减为最小。

原理:对于一个单峰函数,我们可以使用爬山算法来确定其峰值。而多峰函数则不适用。模拟退火与爬山类似,随机选择一个位置,判断其是否优于当前的最优解,不同的是,爬山算法一定会选择保留最优解,而模拟退火算法则会有概率地选择较劣解

实现方式:

1.随机选择新的解

2. 求出新答案

3.根据概率选择接受或者舍弃新答案

#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<cstring>
#define int long long
using namespace std;
int a[510]; //前m+k项为城堡 m项为固定城堡 后为非城堡
bool cn[510];
int f[510][510];
int n,m,k;
int to[510];
int ans;
double T,down=0.95;
void check()
{
     for(int i=1;i<=n;i++)
  {
       printf("%d ",a[i]);
       if(i==m+k) puts("");
  }
   puts("");
}
int calc()
{
   int ret = 0;
   for(int i=m+k+1;i<=n;i++)
  {
       int sr = 11000000000000;
       for(int j=1;j<=m+k;j++)
      {
           sr = min(sr,f[a[j]][a[i]]);
      }
       ret = max(sr,ret);
  }
   return ret;
}
void Floyd()
{
   for(int k=1;k<=n;k++)
       for(int i=1;i<=n;i++)
           for(int j=1;j<=n;j++)
               f[i][j] = min(f[i][j],f[i][k]+f[k][j]);
}
int random(int l,int r)
{
   return rand()%(r-l+1)+l;
}
void SA()
{
   for(T=100000;T>=1e-14;T*=down)
  {
  //     check();
       int x = random(m+1,m+k) , y=random(m+k+1,n);
       swap(a[x],a[y]);
       int d = calc();
       int delta = d - ans;
       if(d<ans) ans=d;
       else if(exp(-delta/T)*RAND_MAX<=rand()) swap(a[x],a[y]);
  }
}
void On_fire()
{
   for(int i=1;i<=80;i++)
   SA();
}
void check_F()
{
   for(int i=1;i<=n;i++)
   for(int j=i+1;j<=n;j++)
   printf("%d %d %d\n",i,j,f[i][j]);
}
signed main()
{
   srand(time(0));
   srand(rand());
   rand();
   memset(f,0x3f,sizeof(f));
   scanf("%lld%lld%lld",&n,&m,&k);
 
   for(int i=1;i<=n;i++)
   scanf("%lld",&to[i]),to[i]++;
   for(int i=1;i<=n;i++)
  {
       int v;
       scanf("%lld",&v);
       f[i][to[i]] =min(f[i][to[i]],v);
       f[to[i]][i] = f[i][to[i]];
       f[i][i] = 0;
  }
   Floyd();
   for(int i=1;i<=m;i++)
  {
       scanf("%lld",&a[i]);
       a[i]++;
       cn[a[i]] = 1;
  }
   int cnt=m;
   for(int i=1;i<=n;i++)
       if(!cn[i]) a[++cnt] = i;
   if(n<=m+k)
  {
     printf("0");
     exit(0);  
  }
   ans = calc();  
   On_fire();
  printf("%lld\n",ans);
}



posted @ 2020-10-24 22:22  岚默笙  阅读(127)  评论(0编辑  收藏  举报