随机化贪心,顾名思义,就是边随机边贪心
这类算法适合解决动态规划类的题目
思想:在贪心原则正确的情况下,随机执行足够多次,就是正解了(涉及到概率学 (玄学 逃:)) ,这里不深究)
不要以为随机到正解几率很小,假设随机到正解的概率为0.1%,我们执行100000次贪心,每次取最优,得到正解的概率是100%
也就是这种贪心也变成了某种意义上的正解
这需要用到我们的random_shuffle函数 复杂度平均为O(n),给数组乱序,用法和sort类似
例子:HAOI 2006 均分数据
已知 n 个正整数 a1,a2...an 。今要将它们分成 m 组,使得各组数据的数值和最平均,即各组的均方差最小。均方差公式如下://感觉貌似有点错,我把我认为对的写上了
[sigma=sqrt{sum_{i=1}^n (x_i-ar{x})^2 over n} ar{x}={sum_{i=1}^n x_i over m}
]
[nsigma^2=sum_{i=1}^n (x_i-ar{x})^2
]
做法:摸你退火,随机贪心,显然摸你退火不太稳,我们就随机贪心吧
对式子变形:(nsigma^2=sum_{i=1}^n(x_i^2-2x_iar x+ar x^2)=sum_{i=1}^nx_i^2-2sum_{i=1}^nx_iar x+sum_{i=1}^nar x^2)
因为(-2ar xsum_{i=1}^nx_i+sum_{i=1}^nar x^2)为定值,可以推测当(sum_{i=1}^nx_i)为定值,每个x尽量接近,(sum_{i=1}^nx_i^2)最大
于是我们可以将数组 random_shuffle 若干次,每次贪心的取值,使每个 x 尽量相等(例如你可以把新加进来的数,加给最小 x),循环个50000次,时间复杂度有点假qwq
代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,m,sum;
int a[21],x[10];
double ans=10000000.0,az;
inline void calc(){
memset(x,0,sizeof(x)); //O(n)
for(int i=1;i<=n;i++){
int p=1;
for(int j=1;j<=m;j++)
if (x[j] < x[p]) p=j;
x[p] += a[i];
}
double now=0;
for(int i=1;i<=m;i++) now += (x[i]-az)*(x[i]-az);
now /= (double)m;
if (now<ans) ans=now;
}
void init(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum+=a[i];
az = (double)sum / m;
for(int i=1;i<=500000;i++)
{
random_shuffle(a+1,a+n+1); //O(n);
calc();
}
}
int main(){
init();
printf("%.2f",sqrt(ans));
}