模拟退火+爬山算法+思维
Posted 钟钟终
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了模拟退火+爬山算法+思维相关的知识,希望对你有一定的参考价值。
模拟退火算法:
归纳几点:
1.是一个随机算法,目的是取得最大值or最小值。首先需要选取初值,一般选为各个点的均值。
2.温度由高到低,每次的变化都伴随x值的跳跃选取
3.对于取最大值来说,若新值大于旧的值,则直接选用新值;若新值小于旧值则根据概率来决定是否跳跃。
4.一直到温度接近0度,停止运算。
P1337 [JSOI2004] 平衡点 / 吊打XXX
#include<bits/stdc++.h>
#define int long long
#define ll long long
#define endl '\\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
#define down 0.996
using namespace std;
const int N=1e6+5;
const int inf=1e18;
const int mod=1e9+7;
int n;
struct node
double x,y,w;
e[N];
double ansx,ansy,answ;
double energy(double x,double y) //能量总和越小越稳定 找最小值
double r=0;
for(int i=1;i<=n;i++)
double dx=x-e[i].x;
double dy=y-e[i].y;
r+=sqrt(dx*dx+dy*dy)*e[i].w;
return r;
void sa()
double t=3000;
while(t>1e-15)
double ax=ansx+(rand()*2-RAND_MAX)*t; // [-32767,+32767]
double ay=ansy+(rand()*2-RAND_MAX)*t;
double aw=energy(ax,ay); //随机跳值的x位置的y值
double ad=aw-answ;
if(ad<0) //新值小于原值,接受
ansx=ax,ansy=ay,answ=aw;
else if(exp(-ad/t)*RAND_MAX>rand()) //新值大于原值,根据概率接受
ansx=ax,ansy=ay;
t*=down;
void solve()
cin>>n;
for(int i=1;i<=n;i++)
cin>>e[i].x>>e[i].y>>e[i].w;
ansx+=e[i].x,ansy+=e[i].y;
ansx/=n,ansy/=n; //均值作为初始值
answ=energy(ansx,ansy);
sa();sa();sa();sa();
cout<<fixed<<setprecision(3)<<ansx<<" ";
cout<<fixed<<setprecision(3)<<ansy<<endl;
signed main()
//ios;
//int T;cin>>T;
//while(T--)
solve();
return 0;
Problem D. Country Meow
各个点的能量总和越小越稳定,因此要枚举出最小点。
#include<bits/stdc++.h>
#define int long long
#define ll long long
#define endl '\\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
#define down 0.996
using namespace std;
const int N=1e6+5;
const int inf=1e18;
const int mod=1e9+7;
int n;
struct node
double x,y,z;
e[N];
double ansx,ansy,ansz,ans=inf;
double dis(node a1,node a2) //能量总和越小越稳定 找最小值
return sqrt((a1.x-a2.x)*(a1.x-a2.x)+(a1.y-a2.y)*(a1.y-a2.y)+(a1.z-a2.z)*(a1.z-a2.z));
void sa()
node k=ansx,ansy,ansz;
double t=3000;
while(t>1e-15)
int g=1;
for(int i=1;i<=n;i++)
if(dis(k,e[i])>dis(k,e[g]))
g=i; //里当前点最远的那个点
double d=dis(k,e[g]);
ans=min(ans,d);
/*k.x+=(e[g].x-k.x)/d*t;
k.y+=(e[g].y-k.y)/d*t;
k.z+=(e[g].z-k.z)/d*t;*/
k.x+=(e[g].x-k.x)*(t/3000);//以一定概率靠近最远的那个点
k.y+=(e[g].y-k.y)*(t/3000);
k.z+=(e[g].z-k.z)*(t/3000);
t*=down;
void solve()
cin>>n;
for(int i=1;i<=n;i++)
cin>>e[i].x>>e[i].y>>e[i].z;
ansx+=e[i].x,ansy+=e[i].y,ansy+=e[i].z;
ansx/=n,ansy/=n,ansz/=n; //均值作为初始值
sa();sa();sa();sa();
cout<<fixed<<setprecision(12)<<ans<<endl;
signed main()
//ios;
//int T;cin>>T;
//while(T--)
solve();
return 0;
P3878 [TJOI2010]分金币
思路:最多有30个金币。
用随机数种子对金币进行分类,重复1000次;再对每种分类交换A、B堆的一个金币,下标也是通过随机数得到,多次做差取最小值。
#include<bits/stdc++.h>
#define int long long
//#define ll long long
#define endl '\\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
#define down 0.996
using namespace std;
const int N=2e6+5;
const int inf=1e18;
const int mod=1e9+7;
int n,a[N],suma,sumb,ans;
vector<int>da,db;
void init()
suma=sumb=0;
da.clear(),db.clear();
for(int i=1;i<=n;i++)
if(rand()&1) da.push_back(a[i]);
else db.push_back(a[i]);
if(abs((int)da.size()-(int)db.size())>1)
while(da.size()>db.size()+1)
db.push_back(da.back());
da.pop_back();
while(db.size()>da.size()+1)
da.push_back(db.back());
db.pop_back();
for(int x:da) suma+=x;
for(int x:db) sumb+=x;
ans=min(ans,abs(suma-sumb));
//cout<<suma<<" "<<sumb<<endl;
void sa()
init();
for(int i=1;i<=1000;i++)
int sa=suma,sb=sumb;
int x=rand()%da.size(),y=rand()%db.size();
sa=sa-da[x]+db[y];
sb=sb-db[y]+da[x];
if(abs(sa-sb)<ans)
ans=abs(sa-sb);
swap(da[x],db[y]);
suma=sa,sumb=sb;
void solve()
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
if(n==1)
cout<<a[1]<<endl;return;
ans=inf;
for(int i=1;i<=1000;i++) sa();
cout<<ans<<endl;
signed main()
//ios;
int T;cin>>T;
while(T--)
solve();
return 0;
C.Removing Smallest Multiples
思路:枚举k值,用标记数组记录处理过的字符串中未出现的数。
不可以跳过一个k的倍数去处理另一个倍数,当时想到了这一点,不知道怎么编代码,真的好笨。。。
#include<bits/stdc++.h>
#define int long long
//#define ll long long
#define endl '\\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
#define down 0.996
using namespace std;
const int N=2e6+5;
const int inf=1e18;
const int mod=1e9+7;
int n,vis[N];
string s;
void solve()
cin>>n>>s;
s=" "+s;
for(int i=1;i<=n;i++) vis[i]=0;
int ans=0;
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j+=i)
if(s[j]=='1') break;
if(vis[j]) continue;
if(s[j]=='0'&&!vis[j])
ans+=i;vis[j]=1;
cout<<ans<<endl;
signed main()
//ios;
int T;cin>>T;
while(T--)
solve();
return 0;
以上是关于模拟退火+爬山算法+思维的主要内容,如果未能解决你的问题,请参考以下文章