• [洛谷P3878][TJOI2010]分金币


    题目大意:把$n(nleqslant30)$个数分成两组,两组个数最多相差$1$,求出两组元素差的绝对值最小使多少

    题解:模拟退火

    卡点:$exp$中的两个数相减写反,导致$exp(x)$中的$x>0$,$exp(x)>1$,相当于一直接受生成的解

    C++ Code:

    #include <algorithm>
    #include <cstdio>
    #include <cmath>
    #define maxn 32
    inline long long abs(long long a) {return a > 0 ? a : -a;}
    int T, n, divn;
    long long sum;
    
    const double ST = 100, delT = 0.9992, eps = 1e-5;
    int Tim = 20;
    struct node {
    	int s[maxn];
    	long long w;
    } ans;
    inline long long calc(node &x) {
    	long long __sum = 0;
    	for (int i = 0; i < divn; i++) __sum += x.s[i];
    	x.w = abs(sum - __sum - __sum);
    	if (n & 1) x.w = std::min(x.w, abs(sum - __sum - __sum - x.s[divn] - x.s[divn]));
    	return x.w;
    }
    inline double rand_d() {return static_cast<double> (rand()) / RAND_MAX;}
    
    void SA() {
    	double T = ST;
    	node now = ans, nxt;
    	while (T > eps) {
    		nxt = now;
    		int x = rand() % n, y = rand() % n;
    		T *= delT;
    		if (x == y) continue;
    		std::iter_swap(nxt.s + x, nxt.s + y);
    		long long del = calc(nxt);
    		if (del < now.w || exp((now.w - del) / T) > rand_d()) now = nxt;
    		if (del < ans.w) ans = nxt;
    	}
    }
    int main() {
    	srand(20040826);
    	scanf("%d", &T);
    	while (T --> 0) {
    		scanf("%d", &n); divn = n >> 1;
    		sum = 0;
    		for (int i = 0; i < n; i++) scanf("%d", ans.s + i), sum += ans.s[i];
    		if (n == 1) {
    			printf("%d
    ", *ans.s);
    			continue;
    		}
    		std::random_shuffle(ans.s, ans.s + n);
    		calc(ans);
    		for (int i = 0; i < Tim; i++) SA();
    		printf("%lld
    ", ans.w);
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    linq
    存储过程动态显示top条数
    js正则表达式
    WebClient异步下载文件
    C++ socket编程基础(ZT)
    Oracle数据导入导出imp/exp命令 10g以上expdp/impdp命令(转载)
    C#获取各种格式的系统时间
    C++中对sprintf()函数的说明(转)
    史上最全前端面试题(含答案)B篇
    常用正则表达式
  • 原文地址:https://www.cnblogs.com/Memory-of-winter/p/10060675.html
Copyright © 2020-2023  润新知