随 机 贪 心

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

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

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

不要以为随机到正解几率很小,假设随机到正解的概率为0.1%,我们执行100000次贪心,每次取最优,得到正解的概率是100%

也就是这种贪心也变成了某种意义上的正解

这需要用到我们的random_shuffle函数 复杂度平均为O(n),给数组乱序,用法和sort类似

例子:HAOI 2006 均分数据
已知 n 个正整数 a1,a2...an 。今要将它们分成 m 组,使得各组数据的数值和最平均,即各组的均方差最小。均方差公式如下://感觉貌似有点错,我把我认为对的写上了

\[\sigma=\sqrt{\sum_{i=1}^n\ (x_i-\bar{x})^2 \over n} \bar{x}={\sum_{i=1}^n\ x_i \over m} \]

\[n\sigma^2=\sum_{i=1}^n\ (x_i-\bar{x})^2 \]

做法:摸你退火,随机贪心,显然摸你退火不太稳,我们就随机贪心吧
对式子变形:\(n\sigma^2=\sum_{i=1}^n(x_i^2-2x_i\bar x+\bar x^2)=\sum_{i=1}^nx_i^2-2\sum_{i=1}^nx_i\bar x+\sum_{i=1}^n\bar x^2\)
因为\(-2\bar x\sum_{i=1}^nx_i+\sum_{i=1}^n\bar 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));
}
posted @ 2020-07-09 15:35  INFP  阅读(346)  评论(2编辑  收藏  举报