模拟退火题单及代码
模拟退火题单
目录
详解算法:https://www.cnblogs.com/linghusama/p/17716814.html
[TJOI2010] 分金币
https://www.luogu.com.cn/problem/P3878
先分类,然后任意交换其中两个币
#include<bits/stdc++.h>
#define int long long
using namespace std;
int w[35];
const int T0=5005;
const double lT=1e-14;
const double d=0.95;
int best,ans,nowans;int n;
int s1,s2,cnt1,cnt2;
int cal(){
s1=0;
s2=0;
for(int i=1;i<=cnt1;i++){
s1+=w[i];
}
for(int i=cnt1+1;i<=n;i++){
s2+=w[i];
}
return abs(s1-s2);
}
void fire(){
best=cal();
int times=1000;
while(times--){
ans=best;
for(double T=T0;T>=lT;T*=d){
int le=rand()%cnt1+1;
int ri=rand()%cnt2+1;
ri+=cnt1;
swap(w[le],w[ri]);
nowans=cal();
if(nowans<best){
best=nowans;
}
if(nowans<ans||1.0*exp((ans-nowans)/T)>=1.0*rand()/RAND_MAX){
ans=nowans;
}
else{
swap(w[le],w[ri]);
}
}
}
cout<<best<<endl;
}
signed main(){
ios::sync_with_stdio(false);
int t;
cin >> t;
srand(time(NULL));
while(t--){
cin >> n;
for(int i=1;i<=n;i++){
cin >> w[i];
}
if(n==1){
cout<<w[1]<<endl;
continue;
}
cnt1=n/2;
cnt2=n/2+(n%2);
fire();
}
}
### RUNAWAY - Run Away
https://www.luogu.com.cn/problem/SP34
常见的二维任意点选取问题
#include<bits/stdc++.h>
using namespace std;
struct node{
double x;
double y;
}p[1005];
/*
求距离所有点的最小值最大
*/
double dis(double x,double y,double xx,double yy){
return sqrt((x-xx)*(x-xx)+(y-yy)*(y-yy));
}
int n;
double limitx,limity;
double cal(double x0,double y0){
double ret=1e9;
for(int i=1;i<=n;i++){
ret=min(ret,dis(x0,y0,p[i].x,p[i].y));
}
return ret;//尝试x0,y0这个坐标下的答案
}
//以上是对答案的计算部分
const int T0=10000;//初始温度
const double d=0.98; //降温系数
const double Tl=1e-14;//最终降到的温度
double ansx,ansy;//最终答案
double RD(double T){
return T*(rand()*2-RAND_MAX);
}
void fire(){
int times=10;//多做几次退火减少偶然性问题
double best=cal(ansx,ansy);//确定自己最初取得的较优解的答案
//注意这里best是写在times之外的,用于表示最终答案的最优值
while(times--){
double ans=best;//ans再times循环内,表示是这一次退火的最终答案
double mayx=ansx;
double mayy=ansy;
for(double T=T0;T>=Tl;T*=d){//模拟降温过程
double xx=mayx+RD(T);
double yy=mayy+RD(T);
//尝试新的答案,新的答案由上一次答案随机得到.
if(xx>limitx||yy>limity||xx<0||yy<0){
continue;//不在范围内的可能性直接byebye
}
double nowans=cal(xx,yy);
if(best<nowans){//如果答案更好,更新答案组成 (同于对比全局的最优值)
best=nowans;
ansx=xx;
ansy=yy;//全局的最优秀
}
if(ans<nowans||exp((ans-nowans)/T)>1.0*rand()/RAND_MAX){//寻找局部最优,要不然答案优秀直接更新,要不然随机化更新以跳出可能的局部最优解问题
ans=nowans;
mayx=xx;
mayy=yy;
}
}
}
}
//以上是模拟退火
int main(){
ios::sync_with_stdio(false);
int t;
cin >> t;
srand(time(NULL));
while(t--){
cin >> limitx >> limity >> n;
ansx=0;
ansy=0;
for(int i=1;i<=n;i++){
cin >> p[i].x >> p[i].y;
ansx+=p[i].x;
ansy+=p[i].y;
}
ansx/=n;
ansy/=n;//刚开始时建立一个初始答案,这里取得平均值。其实就是你感觉什么答案可能有点优秀就可以尝试把它当为初值。
//基本输入
fire();
printf("The safest point is (%.1lf, %.1lf).\n",ansx,ansy);
}
}
[JSOI2016] 炸弹攻击1
https://www.luogu.com.cn/problem/P5544
会打退火就乱做了
#include<bits/stdc++.h>
using namespace std;
struct bd{
double x;
double y;
double r;
}building[15];
struct em{
double x;
double y;
}person[1005];
double dis(double x,double y,double xx,double yy){
return sqrt((x-xx)*(x-xx)+(y-yy)*(y-yy));
}
const double eps=1e-6;
int n,m;
double R;
int cal(double x,double y){
//最大话半径肯定是较好的,就是说要和其他n相切取最小值
double r=1e9;
for(int i=1;i<=n;i++){
double diss=dis(x,y,building[i].x,building[i].y);
diss-=building[i].r;
r=min(r,diss);
}
r=min(r,R);
//然后记录圆的方程来康康在其内部的点有多少
int ret=0;
for(int i=1;i<=m;i++){
if(dis(x,y,person[i].x,person[i].y)<=r){
ret++;
}
}
return ret;
}
const double T0=1e6;
const double d=0.98;
const double LT=1e-15;
int best;
int ans;
double x00,y00;
double RD(double T){
return T*(rand()*2-RAND_MAX);
}
void SA(){
int times=100;
best=cal(x00,y00);
while(times--){
ans=best;
double xx,yy;
xx=x00;
yy=y00;
for(double T=T0;T>=LT;T*=d){
double nowx=xx+RD(T);
double nowy=yy+RD(T);
int res=cal(nowx,nowy);
if(res>best){
best=res;
x00=nowx;
y00=nowy;
xx=nowx;
yy=nowy;
}
if(res>ans||exp((ans-res)/T)<=1.0*rand()/RAND_MAX){
xx=nowx;
yy=nowy;
ans=res;
}
}
}
cout<<best;
}
int main(){
ios::sync_with_stdio(false);
srand(time(NULL));
cin >> n >> m >> R;
for(int i=1;i<=n;i++){
cin >> building[i].x >> building[i].y >> building[i].r;
}
for(int i=1;i<=m;i++){
cin >> person[i].x >> person[i].y;
}
x00=person[1].x;
y00=person[1].y;
SA();
}
「SPOJ4587」FENCE3 - Electric Fences
https://www.luogu.com.cn/problem/SP4587
还是简单呢。
#include<bits/stdc++.h>
using namespace std;
/*
对于一个点和任意一条线,有三种最短情况
一种是左端点
一种是右端点
一种是做垂线
*/
int n;
struct node{
double lex;
double ley;
double rix;
double riy;
}line[205];
double dis(double x,double y,double xx,double yy){
return sqrt((x-xx)*(x-xx)+(y-yy)*(y-yy));
}
double cal(double x,double y){
double ret=0;
for(int i=1;i<=n;i++){
if(line[i].lex==line[i].rix){//竖着的
if(line[i].ley>line[i].riy){
swap(line[i].ley,line[i].riy);
}
if(y>line[i].riy){
ret+=dis(x,y,line[i].lex,line[i].riy);
}
else if(y<line[i].ley){
ret+=dis(x,y,line[i].lex,line[i].ley);
}
else {
ret+=fabs(x-line[i].lex);
}
}
else{//横着的
if(line[i].lex>line[i].rix){
swap(line[i].lex,line[i].rix);
}
if(x>line[i].rix){
ret+=dis(x,y,line[i].rix,line[i].riy);
}
else if(x<line[i].lex){
ret+=dis(x,y,line[i].lex,line[i].ley);
}
else {
ret+=fabs(y-line[i].ley);
}
}
}
return ret;
}
//----------------------------------------
const double T0=1e5;
const double d=0.98;
const double LT=1e-15;
double best;
double ans;
double x00,y00;
double RD(double T){
return T*(rand()*2-RAND_MAX);
}
void SA(){
int times=5;
best=cal(x00,y00);
while(times--){
ans=best;
double xx,yy;
xx=x00;
yy=y00;
for(double T=T0;T>=LT;T*=d){
double nowx=xx+RD(T);
double nowy=yy+RD(T);
double res=cal(nowx,nowy);
if(res<best){
best=res;
x00=nowx;
y00=nowy;
xx=nowx;
yy=nowy;
}
if(res<ans||exp((ans-res)/T)>1.0*rand()/RAND_MAX){
xx=nowx;
yy=nowy;
ans=res;
}
}
}
cout<<fixed<<setprecision(1);
cout<<x00<<" "<<y00<<" "<<best;
}
int main(){
ios::sync_with_stdio(false);
srand(time(NULL));
cin >>n;
for(int i=1;i<=n;i++){
cin >>line[i].lex >>line[i].ley>>line[i].rix>>line[i].riy;
x00+=line[i].lex;
y00+=line[i].ley;
}
// cout<<cal(1.0,1.6)<<endl;
x00/=n;
y00/=n;
SA();
}
锯木厂选址
dp?斜率优化?
爷!不!想!做!
模拟退火是哪两个就好啦
#include<bits/stdc++.h>
using namespace std;
#define int long long
int w[200005];
int d[200005];
int n;
int s;
//以上为基础量
int cal(int a,int b){
return w[a]*d[a]+(w[b]-w[a])*d[b]+(w[n]-w[b])*d[n+1]-s;
}
int best;
int ans;
int a0;int b0;
int aa,bb;
const double T0=20005;
const double TL=1e-15;
const double D=0.98;
int RD(double T){
return round(T*(rand()*2-RAND_MAX));
}
void SA(){
best=cal(a0,b0);
int times=500;
while(times--){
ans=best;
aa=a0;
bb=b0;
for(double T=T0;T>=TL;T*=D){
int nowa=aa+RD(T);
int nowb=bb+RD(T);
nowa=((nowa%n)+n)%n+1;
nowb=((nowb%n)+n)%n+1;
if(nowa==nowb){
continue;
}
if(nowa>nowb){
swap(nowa,nowb);
}
int res=cal(nowa,nowb);
if(res<best){
best=res;
a0=nowa;
b0=nowb;
aa=nowa;
bb=nowb;
ans=res;
}
if(res<ans||exp((ans-res)/T)>1.0*rand()/RAND_MAX){
ans=res;
aa=nowa;
bb=nowb;
}
}
}
cout<<best<<endl;
}
signed main(){
ios::sync_with_stdio(false);
srand(time(NULL));
cin >>n;
w[0]=0;d[0]=0;s=0;
for(int i=1;i<=n;i++)
{
int a,b;
cin >>a >>b;
w[i]=w[i-1]+a;
d[i+1]=d[i]+b;
s=s+a*d[i];
}
a0=1;
b0=2;
SA();
}

浙公网安备 33010602011771号