题解 P3878 【[TJOI2010]分金币】

SIGSEGV

2019-04-05 16:30:13

Solution

~~模拟退火杀人事件~~ 本题使用超级骗分大法(模拟退火)~~想不出正解啊~~ 没有学过模拟退火的右转[吊打XXX](https://www.luogu.org/problemnew/show/P1337) 每次随机选2个组别不同的数,交换它们的组别 看代码吧 ```cpp #include <bits/stdc++.h> using namespace std; int n,v[35],sum1,sum2,grp[3][35],ans,len1,len2;//grp[i][j]:第i组的第j个数 const double D = 0.99551748,E = 1e-8; inline int calc() //计算答案 { return abs(sum1 - sum2); } inline void swap_grp(int g1,int g2) //交换组别 { sum1 -= grp[1][g1];sum2 += grp[1][g1]; sum2 -= grp[2][g2];sum1 += grp[2][g2]; swap(grp[1][g1],grp[2][g2]); } void SA() { double T = 3000; int cur_ans = ans; while (T > E) { int x = rand() % len1 + 1,y = rand() % len2 + 1;//随机获取位置 swap_grp(x,y);int nans = calc(),diff = nans - cur_ans; if (diff < 0) { cur_ans = nans;if (ans > cur_ans) ans = cur_ans; } else if (exp(-diff / T) * RAND_MAX > rand()) //接受更差的新的解 cur_ans = nans; else swap_grp(x,y); T *= D;//降温 } } int main () { srand(time(0)); int T; scanf("%d",&T); while (T--) { scanf("%d",&n); sum1 = sum2 = 0;//sum:组内的数的和 len1 = n / 2;len2 = n - len1;//len:分组的长度 for (int i = 1;i <= n;i++) { scanf("%d",&v[i]);//默认前半部分分一组,后一半分一组 if (i <= len1) {grp[1][i] = v[i];sum1 += v[i];} else {grp[2][i - len1] = v[i];sum2 += v[i];} } ans = calc(); if (n == 1) //此处要特判,不然len1会为0 取模时会报RE 我被坑了11次 { printf("%d\n",ans);continue; } for (int i = 1;i <= 15;i++) SA(); printf("%d\n",ans); } return 0; } ```