当然,货仓选址的经典做法是排序,从数组中选择中位数
,以这个数为货仓求解即可。
那么,模拟退火能不能搜出来解呢?
下面是代码:
货仓选址– 模拟退火
#include<bits/stdc++.h>
using namespace std ;
typedef long long LL ;
const int N = 1E5 +10 ;
int n ;
int q[N] ;
double ans = 1e8 ;
double rand(double l , double r)
{
return (double) rand() / RAND_MAX * (r-l) + l ;
}
double calc(double x)
{
double res = 0 ;
for(int i = 0 ; i < n ; i ++ )
{
res += fabs(q[i] - x) ;
}
ans = min(ans , res) ;
ans = res ;
return res ;
}
void simulate_anneal()
{
int x = rand(0,40000) ;
for(double t = 40000 ; t > 1e-4 ; t *= 0.9)
{
int np = rand(x - t ,x + t ) ;
int dx = calc(np) - calc(x) ;
if(exp(-dx / t ) > rand(0,1) ) x = np ;
}
}
int main()
{
int start = clock() ;
srand(time(NULL)) ;
cin >> n ;
for(int i = 0 ; i < n ; i ++ )
{
scanf("%d" , &q[i]) ;
}
for(int i = 0 ; i < 1000 && clock() - start < 0.9 * CLOCKS_PER_SEC; i ++ )
{
simulate_anneal() ;
}
printf("%.0lf\n" , ans) ;
return 0 ;
}
然而最最欧气的时候
也仅仅是
针对此题,对模拟退火的修正:
1. 排序,使得全局函数呈现单凹
2. 将依概率迁移
修正为只有更优才迁移
3. 卡时设定为0.8s
AC 代码
#include<bits/stdc++.h>
using namespace std ;
typedef long long LL ;
const int N = 1E5 +10 ;
int n ;
int q[N] ;
double ans = 1e8 ;
double rand(double l , double r)
{
return (double) rand() / RAND_MAX * (r-l) + l ;
}
double calc(double x)
{
double res = 0 ;
for(int i = 0 ; i < n ; i ++ )
{
res += fabs(q[i] - x) ;
}
ans = min(ans , res) ;
ans = res ;
return res ;
}
void simulate_anneal()
{
int x = rand(0,40000) ;
for(double t = 40000 ; t > 1e-12 ; t *= 0.9)
{
int np = rand(x - t ,x + t ) ;
int dx = calc(np) - calc(x) ;
if(dx < 0 ) x = np ;
}
}
int main()
{
double start = clock() ;
srand(time(NULL)) ;
scanf("%d",&n) ;
for(int i = 0 ; i < n ; i ++ )
{
scanf("%d" , &q[i]) ;
}
sort(q , q+n) ;
for(int i = 0 ; i < 1000 && clock() - start < 0.8 * CLOCKS_PER_SEC; i ++ )
{
simulate_anneal() ;
}
printf("%.0lf\n" , ans) ;
return 0 ;
}
偶尔会有超时现象
以下代码调优了参数,必AC,而且省了排序。
一定可以AC的初始化种子比如1000
#include<bits/stdc++.h>
using namespace std ;
const int N = 1e5+10 ;
int n ;
int q[N] ;
double ans = 1e10 ;
int start = clock() ;
double rand(double l , double r)
{
return (double)rand() / RAND_MAX *(r-l) + l ;
}
int calc(int x)
{
double res = 0 ;
for(int i = 0 ; i < n ; i++ )
{
res += abs(q[i] - x) ;
}
ans = min(ans , res ) ;
return res ;
}
void simulate_anneal()
{
double x = rand(0 , 40000) ;
for(double t = 1e4 ; t > 1 ; t *= 0.9) // 我们不需要精确到小数级别
{
auto np = rand(x - t , x + t ) ;
auto dx = calc(np) - calc(x) ;
if(dx < 0) x = np ;
}
}
int main()
{
srand(time(NULL)) ;
scanf("%d",&n) ;
for(int i = 0 ; i < n ; i ++ )
{
scanf("%d",&q[i]) ;
}
for(int i = 0 ; i < 1000 && clock() - start < 0.8 * CLOCKS_PER_SEC ; i++ ) simulate_anneal() ;
printf("%.0lf\n" , ans) ;
return 0 ;
}
我有一个问题就是 要证明他是单峰最值函数才能用 if(dx < 0 ) x= np 吧,不然会陷入局部最优解
我就知道这样的题目 答主一定会出现,你是那样拉风的男人,不管在什么地方,就好像漆黑中的萤火虫一样,是那样的鲜明、出众。你那忧郁的眼神,唏嘘的胡渣子,神乎其技的A法,还有那杯Dry martine,都深深的迷住了我。