首页 > 其他分享 >2680. 均分数据

2680. 均分数据

时间:2022-10-02 10:22:54浏览次数:76  
标签:le 数据 int double long 均分 模拟退火 2680 define

题目链接

2680. 均分数据

已知 \(N\) 个正整数:\(A_1、A_2、……、A_n\)。

今要将它们分成 \(M\) 组,使得各组数据的数值和最平均,即各组的均方差最小。

均方差公式如下:

\(\sigma = \sqrt{\frac{\sum_{i=1}^n(x_i - \bar{x})^2}{n}},\bar{x} = \frac{\sum_{i=1}^n x_i}{n}\)

其中 \(\sigma\) 为均方差,\(\bar{x}\) 为各组数据和的平均值,\(x_i\) 为第 \(i\) 组数据的数值和。

输入格式

第一行是两个整数,表示 \(N,M\) 的值(\(N\) 是整数个数,\(M\) 是要分成的组数)。

第二行有 \(N\) 个整数,表示 \(A_1、A_2、……、A_n\)。(同一行的整数间用空格分开)

输出格式

包括一行,这一行只包含一个数,表示最小均方差的值(保留小数点后两位数字)。

数据范围

\(M \le N \le 20\),
\(2 \le M \le 6\),
\(1 \le A_i \le 50\)

输入样例:

6 3
1 2 3 4 5 6

输出样例:

0.00

解题思路

模拟退火

将每个数放入某一组中有一个贪心策略:每次将该数放和最小的组中

这样的策略不一定能构造出答案,但是如果每次模拟退火将序列随机化就一定可以构造出答案,另外构造出的序列交换两点变化不大,即函数具有一定的连续性,故可用模拟退火求解

  • 时间复杂度:\(O(随机)\)

代码

// Problem: 均分数据
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/2682/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=25,M=10;
int n,m,w[N],s[N];
double res=1e9;
double cal()
{
	memset(s,0,sizeof s);
	for(int i=0;i<n;i++)
	{
		int k=0;
		for(int j=0;j<m;j++)
			if(s[j]<s[k])k=j;
		s[k]+=w[i];
	}
	double avg=0,ret=0;
	for(int i=0;i<m;i++)avg+=(double)s[i]/m;
	for(int i=0;i<m;i++)ret+=(s[i]-avg)*(s[i]-avg);
	res=min(res,sqrt(ret/m));
	return ret;
}
void aimulate_anneal()
{
	random_shuffle(w,w+n);
	for(double t=1e4;t>1e-4;t*=0.99)
	{
		int a=rand()%n,b=rand()%n;
		double x=cal();
		swap(w[a],w[b]);
		double y=cal();
		double delta=y-x;
		if(exp(-delta/t)<(double)rand()/RAND_MAX)swap(w[a],w[b]);	
	}
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<n;i++)cin>>w[i];
    while((double)clock()/CLOCKS_PER_SEC<0.8)aimulate_anneal();
    printf("%.2lf",res);
    return 0;
}

标签:le,数据,int,double,long,均分,模拟退火,2680,define
From: https://www.cnblogs.com/zyyun/p/16748319.html

相关文章