2021.11.01 模拟退火
关于模拟退火的学习笔记 - 李有才99NL的博客 - 洛谷博客 (luogu.org)
题解 P1337 【[JSOI2004]平衡点 / 吊打XXX】 - SuperJvRuo 的博客 - 洛谷博客 (luogu.com.cn)
随机算法_模拟退火算法 - March On - 博客园 (cnblogs.com)
练习题:
P1337 [JSOI2004]平衡点 / 吊打XXX - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<ctime>
using namespace std;
const int N=1010;
const double down=0.996;
int n;
double ansx,ansy,answ;
struct node{
double x,y,w;
}a[N];
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')w=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0'){
s=s*10+ch-'0';
ch=getchar();
}
return s*w;
}
inline double energy(double x,double y){
double fin=0;
for(int i=1;i<=n;i++){
double xi=x-a[i].x,yi=y-a[i].y;
fin+=sqrt(xi*xi+yi*yi)*a[i].w;
}
return fin;
}
inline void simulate_anneal(){
double t=3000;
while(t>1e-14){
double xi=ansx+(rand()*2-RAND_MAX)*t;
double yi=ansy+(rand()*2-RAND_MAX)*t;
double wi=energy(xi,yi);
double cha=wi-answ;
if(cha<0)ansx=xi,ansy=yi,answ=wi;
else if(exp(-cha/t)*RAND_MAX>rand())ansx=xi,ansy=yi;
// exp(x):e^x
t*=down;
}
}
inline void SA(){
simulate_anneal();simulate_anneal();simulate_anneal();
}
int main(){
n=read();
for(int i=1;i<=n;i++){
a[i].x=read(),a[i].y=read(),a[i].w=read();
ansx+=a[i].x;ansy+=a[i].y;
}
ansx/=n;ansy/=n;answ=energy(ansx,ansy);
SA();
printf("%.3lf %.3lf",ansx,ansy);
return 0;
}
P2210 Haywire - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
//随机一个数列,不断计算答案,交换元素,得到最优解
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<ctime>
using namespace std;
const double down=0.996;
const double MAX_TIME=0.996;
const int N=20;
int n,a[N][4],pos[N];
int ans=0x7fffffff;
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')w=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0'){
s=s*10+ch-'0';
ch=getchar();
}
return s*w;
}
inline int energy(){
int fin=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=3;j++)
fin+=abs(pos[i]-pos[a[i][j]]);
return fin/2;
}
inline void simulate_anneal(){
double t=3000;
while(t>1e-15){
int x=rand()%n+1,y=rand()%n+1;
swap(pos[x],pos[y]);
int ansi=energy();
int cha=ansi-ans;
if(cha<0)ans=ansi;
else if(exp(-cha/t)*RAND_MAX<=rand())swap(pos[x],pos[y]);
t*=down;
}
}
inline void SA(){
while((double)clock()/CLOCKS_PER_SEC<MAX_TIME)simulate_anneal();
//卡时专用,但是容易写挂,oi wiki上说“这里的 MAX_TIME 是一个自定义的略小于时限的数”,到底是多少?
}
int main(){
n=read();
for(int i=1;i<=n;i++){
for(int j=1;j<=3;j++)a[i][j]=read();
pos[i]=i;
}
ans=energy();
SA();
cout<<ans;
return 0;
}
[P2503 HAOI2006]均分数据 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
注:忽然明白模拟退火就是一种模拟,十分暴力和随机,根本不讲究算法
// 随机分组??
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<cstring>
using namespace std;
const int N=25;
const double down=0.996;
const double MAX_TIME=0.99;
int n,m,belong[N];
double tot[N],a[N];
double ans=0x3f3f3f3f,average;
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')w=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0'){
s=s*10+ch-'0';
ch=getchar();
}
return s*w;
}
inline double energy(){
double fin=0;
for(int i=1;i<=m;i++)fin+=(tot[i]-average)*(tot[i]-average);
fin/=(double)m;//fin=sqrt(fin);
return fin;
}
inline void change(int x,int to){
tot[belong[x]]-=a[x];
tot[to]+=a[x];
belong[x]=to;
}
inline void simulate_anneal(){
double t=3000,ansii=ans;
while(t>1e-15){
int x=rand()%n+1,y=rand()%m+1;
int yi=belong[x];
change(x,y);
double ansi=energy();
double cha=ansi-ans;
if(cha<0){
ans=ansi;
//if(ans>ansii)ans=ansii;
}
else if(exp(-cha/t)*RAND_MAX<=rand())change(x,yi);
t*=down;
}
}
inline void SA(){
while((double)clock()/CLOCKS_PER_SEC<MAX_TIME)simulate_anneal();
}
int main(){
srand(time(0));
n=read();m=read();
for(int i=1;i<=n;i++){
a[i]=read();
average+=a[i];
int x=rand()%m+1;
belong[i]=x;
tot[x]+=a[i];
}
average/=m;
ans=energy();
SA();
printf("%.2lf",(double)sqrt(ans));
return 0;
}
[P5544 JSOI2016]炸弹攻击1 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<ctime>
using namespace std;
const int N=1e3+10;
const double down=0.996;
const double MAX_TIME=0.96;
int n,m,r;
double ansx,ansy;
int ans;
struct node{
double x,y,r;
}build[15];
struct nodei{
double x,y;
}enemy[N];
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')w=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0'){
s=s*10+ch-'0';
ch=getchar();
}
return s*w;
}
inline double calc(double x,double y,double u,double v){
return (double)(sqrt((x-u)*(x-u)+(y-v)*(y-v)));
}
int energy(double x,double y){
int fin=0;
double dis=r;
for(int i=1;i<=n;i++)dis=min(dis,calc(x,y,build[i].x,build[i].y)-build[i].r);
for(int i=1;i<=m;i++)if(dis>=calc(x,y,enemy[i].x,enemy[i].y))++fin;
return fin;
}
inline void simulate_anneal(){
double t=3000;
while(t>1e-15){
double x=ansx+(rand()*2-RAND_MAX)*t;
double y=ansy+(rand()*2-RAND_MAX)*t;
int ansi=energy(x,y);
int cha=ansi-ans;
if(cha>0)ansx=x,ansy=y,ans=ansi;
else if(exp(-cha/t)*RAND_MAX<rand())ansx=x,ansy=y;
//这里与别的不同,因为这里要求ans越大越好
t*=down;
}
///cout<<ans<<endl;
}
inline void SA(){
while((double)clock()/CLOCKS_PER_SEC<MAX_TIME)simulate_anneal();
}
int main(){
n=read();m=read();r=read();
for(int i=1;i<=n;i++)
build[i].x=(double)read(),build[i].y=(double)read(),build[i].r=(double)read();
for(int i=1;i<=m;i++)
enemy[i].x=(double)read(),enemy[i].y=(double)read(),ansx+=enemy[i].x,ansy+=enemy[i].y;
ansx/=(double)m,ansy/=(double)m;
ans=energy(ansx,ansy);
//cout<<ans<<endl;
SA();
cout<<ans;
return 0;
}
[P3878 TJOI2010]分金币 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
30分TLE代码
// 我寻思着又是一个随机分组,但是二进制遍历似乎也可以
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<cstring>
using namespace std;
const int N=30;
int t,n,a[N],belong[N],sum[4],ans=0x3f3f3f3f;
const double down=0.9112;
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')w=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0'){
s=s*10+ch-'0';
ch=getchar();
}
return s*w;
}
inline int energy(){
int sum1=0,sum2=0;
for(int i=1;i<=(n+1)/2;i++)sum1+=a[i];
for(int i=(n+1)/2+1;i<=n;i++)sum2+=a[i];
return abs(sum1-sum2);
}
inline void simulate_anneal(){
double t=5000;
while(t>1e-10){
int x=rand()%n+1,y=rand()%n+1;
swap(a[x],a[y]);
int ansi=energy();
int cha=ansi-ans;
if(cha<0)ans=ansi;
else if(exp(-cha/t)*RAND_MAX<=rand())swap(a[x],a[y]);
t*=down;
}
}
inline void SA(){
for(int i=1;i<=1000;i++)simulate_anneal();
}
int main(){
t=read();
while(t--){
memset(a,0,sizeof(a));
n=read();
if(n==1){
cout<<"0"<<endl;
continue;
}
for(int i=1;i<=n;i++)a[i]=read();
ans=energy();
SA();
cout<<ans<<endl;
}
return 0;
}
别人ac代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define inf 2147483647
#define re register
using namespace std;
int n,ans=inf,a[1005];
int get()
{
int sum1=0,sum2=0;
for (re int i=1;i<=(n+1)/2;i++)
sum1+=a[i];
for (re int i=(n+1)/2+1;i<=n;i++)
sum2+=a[i];
return abs(sum1-sum2);
}
void SA()
{
double beginT=5000,endT=1e-10,changeT=0.9112;
for (re double T=beginT;T>endT;T*=changeT)
{
int x=rand()%n+1, y=rand()%n+1;
swap(a[x],a[y]);
int sum=get();
if (sum<ans)
ans=sum;
else
if (exp((ans-sum)/T)<(double(rand())/RAND_MAX))
swap(a[x],a[y]);
}
}
int main()
{
srand(rand());
int T;
cin>>T;
while (T--)
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
int ctrl=1000;
while(ctrl--)
SA();
cout<<ans<<endl;
ans=inf;
}
}
posted on
浙公网安备 33010602011771号