随 机 贪 心

随机化贪心,顾名思义,就是边随机边贪心

这类算法适合解决动态规划类的题目

思想:在贪心原则正确的情况下,随机执行足够多次,就是正解了(涉及到概率学 (玄学 逃:)) ,这里不深究)

不要以为随机到正解几率很小,假设随机到正解的概率为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));
}
原文地址:https://www.cnblogs.com/shikeyu/p/13274014.html