1 条题解

  • 0
    @ 2025-8-24 21:38:24

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar ysner
    这个家伙不懒,但也什么都没有留下

    搬运于2025-08-24 21:38:24,当前版本为作者最后更新于2018-03-22 20:06:17,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    这题好像只能试出答案。。。

    于是,时间复杂度低却又玄学的搜索“模拟退火”走起。

    这不连续分组看起来很不好弄,我们可以把它转化为一个熟悉的问题:连续分组问题。这个问题可以用简单DP完成(设状态为f[i][j],表示前i个数分j组)。

    怎么实现转化呢?用于连续分组的序列 改下顺序 不就成了不连续分组的问题吗?

    所以,用模拟退火rand序列,再DP统计答案,取其小者即可。

    如果通不过就改改SA中的参数吧。

    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #include<ctime>
    #define ll long long
    #define re register
    #define il inline
    #define pf(x) ((x)*(x))
    #define fp(i,a,b) for(re int i=a;i<=b;i++)
    #define fq(i,a,b) for(re int i=a;i>=b;i--)
    using namespace std;
    const int N=50;
    int n,m,a[N],s[N];
    double ans=1e100,f[N][N],ysn;
    il int gi()
    {
      re int x=0,t=1;
      re char ch=getchar();
      while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
      if(ch=='-') t=-1,ch=getchar();
      while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
      return x*t;
    }
    il double work()
    {
      memset(f,127,sizeof(f));
      fp(i,1,n) s[i]=s[i-1]+a[i];//注意到数列不固定,因此不能预处理
      f[0][0]=0;
      fp(i,1,n)
        fp(j,1,i)
        fp(k,0,i-1)
        f[i][j]=min(f[i][j],f[k][j-1]+pf(s[i]-s[k]-ysn));
      ans=min(ans,f[n][m]);
      return f[n][m];
    }//DP处理rand出的序列
    il double Rand(){return rand()%100000/100000.00;}
    il void SA(re double T)
    {
      double now=ans;
      while(T>1e-3)
        {
          re int x=rand()%n+1,y=rand()%n+1;
          if(x==y) continue;
          swap(a[x],a[y]);//rand出新序列
          re double  nw=work();
          if(nw<now||exp(now-nw)/T>Rand()) now=nw;//一定概率接受当前状态
          else swap(a[x],a[y]);
          T*=0.99;
        }
      fp(i,1,10000)
        {
          re int x=rand()%n+1,y=rand()%n+1;
          if(x==y) continue;
          swap(a[x],a[y]);work();swap(a[x],a[y]);
        }
    }
    il double Time(){return (double)clock()/CLOCKS_PER_SEC;}
    int main()
    {
      srand(19260817);
      n=gi();m=gi();
      fp(i,1,n) a[i]=gi();
      fp(i,1,n) ysn+=1.0*a[i]/m;
      work();
      while(Time()<0.75)//通过增加rand的次数保证正确性
        SA(10000);
      printf("%.2lf\n",sqrt(ans/m));
      return 0;
    }
    
    
    • 1

    信息

    ID
    1541
    时间
    1000ms
    内存
    125MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者