1 条题解
-
0
自动搬运
来自洛谷,原作者为

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
- 上传者