zoukankan      html  css  js  c++  java
  • [BZOJ 2428] [HAOI2006] 均分数据

    题目链接:BZOJ - 2428

    题目分析

    这道题使用随机化算法来做,可以使用模拟退火,也可以random_shuffle之后贪心。

    模拟退火:

    要进行多次模拟退火,每次进行模拟退火之前,给每个点随机分配一下集合。

    然后模拟退火的随机移动就是随机一个点,再找另一个集合y,将这个点移动到集合y中。

    开始时模拟退火的移动导致答案的变动幅度会比较大,这时候集合y就直接取最小的集合,到后期就随机一个集合y。

    注意,如果移动之后答案与当前答案相同,也要移动过去。这是为了避免滞留在平坦的高原地形上

    random_shuffle + 贪心:

    多次进行random_shuffle + 贪心的过程,可能就会找到最优解了= =

    代码

    模拟退火:

    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    
    using namespace std;
    
    const int MaxN = 20 + 5;
    
    typedef double DB;
    
    int n, m;
    int A[MaxN], Belong[MaxN];
    
    DB Ans, Ave;
    DB Sum[MaxN];
    
    inline DB Sqr(DB x) {return x * x;}
    
    inline DB Rand() 
    {
    	return (DB)(rand() % 10000) / 10000.0;
    }
    
    void SA() 
    {
    	memset(Sum, 0, sizeof(Sum));
    	for (int i = 1; i <= n; ++i) 
    	{	
    		Belong[i] = rand() % m + 1;
    		Sum[Belong[i]] += (DB)A[i];
    	}
    	DB T = 10000;
    	int t, x, y;
    	DB DE, Temp, Num;
    	Num = 0;
    	for (int i = 1; i <= m; ++i) Num += Sqr(Sum[i] - Ave);
    	if (Num < Ans) Ans = Num;
    	while (T > 0.1)
    	{
    		T *= 0.9;
    		t = rand() % n + 1;
    		x = Belong[t];
    		if (T > 500)
    		{
    			y = 1; Temp = Sum[1];
    			for (int i = 2; i <= m; ++i) 
    			{
    				if (Sum[i] < Temp)
    				{
    					Temp = Sum[i];
    					y = i;
    				}
    			}
    		}
    		else y = rand() % m + 1;
    		if (y == x) continue;
    		Temp = Num;
    		Temp -= Sqr(Sum[x] - Ave) + Sqr(Sum[y] - Ave);
    		Temp += Sqr(Sum[x] - A[t] - Ave) + Sqr(Sum[y] + A[t] - Ave);
    		if (Temp < Ans) Ans = Temp;
    		DE = Num - Temp;
    		if (DE >= 0 || Rand() < exp(DE / T))
    		{
    			Num = Temp;
    			Sum[x] -= A[t];
    			Sum[y] += A[t];
    			Belong[t] = y;
    		}
    	}
    	if (Num < Ans) Ans = Num;
    }
    
    int main()
    {
    	srand(80458946);
    	scanf("%d%d", &n, &m);
    	for (int i = 1; i <= n; ++i) 
    	{	
    		scanf("%d", &A[i]);
    		Ave += (DB)A[i];
    	}
    	Ave /= m;
    	Ans = 1e50;
    	for (int i = 1; i <= 10000; ++i) SA();
    	printf("%.2lf
    ", sqrt(Ans / m));
    	return 0;
    }
    

     

    random_shuffle + 贪心:

    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    
    using namespace std;
    
    const int MaxN = 20 + 5;
    
    typedef double DB;
    
    int n, m;
    int A[MaxN];
    
    inline DB Sqr(DB x) {return x * x;}
    
    DB Ave, Ans;
    
    DB Solve()
    {
    	DB p, q, Sum = 0, ret = 0, Cnt = 1;
    	int i;
    	for (i = 1; i <= n && Cnt < m; ++i)
    	{
    		if (Sum + (DB)A[i] >= Ave)
    		{
    			p = Sqr(Sum + (DB)A[i] - Ave);
    			q = Sqr(Sum - Ave);
    			if (p < q) 
    			{
    				ret += p;
    				Sum = 0;
    			}
    			else
    			{
    				ret += q;
    				Sum = A[i];
    			}
    			++Cnt;
    		}
    		else Sum += (DB)A[i];
    	}
    	for (; i <= n; ++i) Sum += (DB)A[i];
    	ret += Sqr(Sum - Ave);
    	return sqrt(ret / m);
    }
    
    int main()
    {
    	scanf("%d%d", &n, &m);
    	for (int i = 1; i <= n; ++i) 
    	{	
    		scanf("%d", &A[i]);
    		Ave += (DB)A[i];
    	}
    	Ave /= m;
    	Ans = 1e50;
    	for (int i = 1; i <= 500000; ++i)
    	{
    		random_shuffle(A + 1, A + n + 1);
    		Ans = min(Ans, Solve());
    	}
    	printf("%.2lf
    ", Ans);
    	return 0;
    }
    

      

  • 相关阅读:
    Ubuntu安装软件问题的解决
    寻找两个字符串中最长的公共部分字符串
    CentOS
    vim自定义配置
    git创建远程仓库以及在本地提交到远程仓库的方法
    黑金开发板在NiosII环境下烧写image到flash失败的解决办法
    f.lux 一款免费的护眼开源软件
    python 制作自动化脚本
    修改python 默认的存储路径
    第一篇博客
  • 原文地址:https://www.cnblogs.com/JoeFan/p/4342703.html
Copyright © 2011-2022 走看看