模拟退火+爬山算法+思维

模拟退火+爬山算法+思维,第1张

CSDN话题挑战赛第2期

模拟退火算法:

归纳几点:
1.是一个随机算法,目的是取得最大值or最小值。首先需要选取初值,一般选为各个点的均值。
2.温度由高到低,每次的变化都伴随x值的跳跃选取
3.对于取最大值来说,若新值大于旧的值,则直接选用新值;若新值小于旧值则根据概率来决定是否跳跃。
4.一直到温度接近0度,停止运算。

P1337 [JSOI2004] 平衡点 / 吊打XXX
#include
#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
#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
#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<
}
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
#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;
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/3002726.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-09-27
下一篇 2022-09-27

发表评论

登录后才能评论

评论列表(0条)

保存