[返回模拟退火略解]

题目描述

今有 nnn 个数 {ai}\{a_i\}{ai​},把它们分成两堆{X},{Y}\{X\},\{Y\}{X},{Y},求一种分配使得∣∑i∈Xai−∑i∈Yai∣|\sum_{i\in X}{a_i}-\sum_{i\in Y}{a_i}|∣i∈X∑​ai​−i∈Y∑​ai​∣的值最小。

Solution 3878\text{Solution 3878}Solution 3878 解法一

模拟退火SA。

尝试重新排列 aaa,将 aaa 的前半部分分成一堆,后半部分分成一堆,求出解。

贴上 BriMon dalao的代码。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <cmath>
using namespace std;
#define reg register
inline int read() {
int res = 0;char ch=getchar();bool fu=0;
while(!isdigit(ch)) {if(ch=='-')fu=1;ch=getchar();}
while(isdigit(ch)) res=(res<<3)+(res<<1)+(ch^48), ch=getchar();
return fu?-res:res;
} int T, n;
int a[35];
int ans; inline int Calc()
{
int res1 = 0, res2 = 0;
for (reg int i = 1 ; i <= n ; i ++)
if (i <= (n + 1) / 2) res1 += a[i];
else res2 += a[i];
return abs(res1 - res2);
} inline void SA()
{
double T = 2333.0;
while(T > 1e-9)
{
int x = rand() % ((n + 1) / 2) + 1, y = rand() % ((n + 1) / 2) + ((n + 1) / 2);
if (x <= 0 or x > n or y <= 0 or y > n) continue;
swap(a[x], a[y]);
int newans = Calc();
int dert = ans - newans;
if (dert > 0) ans = newans;
else if (exp((double)((double)dert/T)) * RAND_MAX <= rand()) swap(a[x], a[y]);
T *= 0.998;
}
} int main()
{
T = read();
srand((unsigned)time(NULL));
while(T--)
{
n = read();
for (reg int i = 1 ; i <= n ; i ++) a[i] = read();
ans = 1e9;
for (int i = 1 ; i <= 50 ; i ++) SA();
cout << ans << endl;
}
return 0;
}

Solution 3878\text{Solution 3878}Solution 3878 解法二

尝试 dfs 剪枝。

每个金币有取和不取 222 种状态,最多 303030 个金币,深搜需 2302^{30}230 的时间。然而可以优化。

按价值从大到小排序,你一不小心取的价值太大会被剪枝。

最多取 n2\frac{n}{2}2n​ 个金币,你取得太多是要被剪枝的。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm> #define reg register typedef long long ll;
int T,n;
ll a[40],s,ans;
bool b[40];
int hh[40]; int cmp(int a,int b){
return a>b;
}
ll h(int x,int y){
return hh[y]-hh[x-1];
}
ll dfs(int c,int x,ll X,int y,ll Y)
{
if(x>n/2||y>n/2) return ans;
ll nx=X+h(c,c+(n/2-x)-1);
if(nx<=s-nx) return(s-nx-nx);
nx=X+h(n-(n/2-x)+1,n);
if(nx>=s-nx) return(nx-(s-nx));
ll p=dfs(c+1,x+1,X+a[c],y,Y);
ll q=dfs(c+1,x,X,y+1,Y+a[c]);
if(p<q) return p;
return q;
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d",&n);
s=0;ans=1e17;
for(reg int i=1;i<=n;++i){
scanf("%lld",&a[i]);
s+=a[i];
}
if(n%2){
++n;
a[n]=0;
}
std::sort(a+1,a+n+1,cmp);
for(reg int i=1;i<=n;++i)
hh[i]=hh[i-1]+a[i];
printf("%lld\n",dfs(1,0,0,0,0));
}
}

luogu P3878 [TJOI2010]分金币的更多相关文章

  1. [洛谷P3878][TJOI2010]分金币

    题目大意:把$n(n\leqslant30)$个数分成两组,两组个数最多相差$1$,求出两组元素差的绝对值最小使多少 题解:模拟退火 卡点:$\exp$中的两个数相减写反,导致$\exp(x)$中的$ ...

  2. [luogu3878][TJOI2010]分金币【模拟退火】

    题目描述 现在有n枚金币,它们可能会有不同的价值,现在要把它们分成两部分,要求这两部分金币数目之差不超过1,问这样分成的两部分金币的价值之差最小是多少? 分析 根据模拟退火的基本套路,先随机分两堆金币 ...

  3. [TJOI2010]分金币

    嘟嘟嘟 看数据范围,就能想到折半搜索. 但怎么搜,必须得想清楚了. 假设金币总数为1000,有20个人,首先搜前10个人,把答案记下来.然后如果在后十个人中搜到了4个人,价值为120,那么我们应该在记 ...

  4. [Luogu3878] [TJOI2010]分金币

    题目描述 现在有n枚金币,它们可能会有不同的价值,现在要把它们分成两部分,要求这两部分金币数目之差不超过1,问这样分成的两部分金币的价值之差最小是多少? 输入输出格式 输入格式: 每个输入文件中包含多 ...

  5. Luogu-3878 [TJOI2010]分金币

    这题和在我长郡考试时的一道题思路差不多...考虑折半搜索,预处理左半边选的方案所产生的数量差值\(x\)以及价值差值\(y\),把\(y\)扔到下标为\(x\)的set里面,然后在搜索右半边,每搜出一 ...

  6. 分金币 bzoj 3293

    分金币(1s 128M)  coin [问题描述] 圆桌上坐着n个人,每人有一定数量的金币,金币总数能被n整除.每个人可以给他左右相邻的人一些金币,最终使得每个人的金币数目相等.你的任务是求出被转手的 ...

  7. 【BZOJ-3293&1465&1045】分金币&糖果传递×2 中位数 + 乱搞

    3293: [Cqoi2011]分金币 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 854  Solved: 476[Submit][Status] ...

  8. 【贪心+中位数】【UVa 11300】 分金币

    (解方程建模+中位数求最短累积位移) 分金币(Spreading the Wealth, UVa 11300) 圆桌旁坐着n个人,每人有一定数量的金币,金币总数能被n整除.每个人可以给他左右相邻的人一 ...

  9. 【BZOJ3293】分金币(贪心)

    [BZOJ3293]分金币(贪心) 题面 BZOJ 洛谷 题解 和上一题一样啊. #include<cstdio> #include<cmath> #include<al ...

随机推荐

  1. 体验一下:AndroidX

    背景 今天在更新 ButterKnife 到最新版本的时候出现了一个问题,我引入的是 10.1.0 最新版本: implementation 'com.jakewharton:butterknife: ...

  2. Oracle SQL调优之绑定变量用法简介

    目录 一.SQL执行过程简介 二.绑定变量典型用法 2.1.在SQL中绑定变量 2.2.在PL/SQL中使用绑定变量 2.3.PL/SQL批量绑定变量 2.4.Java代码里使用绑定变量 最近在看&l ...

  3. setuptools的安装

    Python 2.x:    sudo apt-get install python-setuptools           (python2.x版本执行此命令) Python 3.x:    su ...

  4. Day 22 进程管理2之系统的平均负载

    1.管理进程状态 当程序运行为进程后,如果希望停止进程,怎么办呢? 那么此时我们可以使用linux的kill命令对进程发送关闭信号.当然除了kill.还有killall,pkill 1.使用kill ...

  5. [VB.NET Tips]字符串转换为日期

    有些字符串需要转换成日期,或者整型转换为日期,可以参考如下思路: Dim result As Date Dim source As String = "20190515" resu ...

  6. 理解Vue.mixin,带你正确的偷懒

    关于Vue.mixin在vue官方文档中是这么解释的: 混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能.一个混入对象可以包含任意组件选项.当组件使用混入对象时,所有 ...

  7. 四、springBoot 优雅的创建定时任务

    前言 好几天没写了,工作有点忙,最近工作刚好做一个定时任务统计的,所以就将springboot 如何创建定时任务整理了一下. 总的来说,springboot创建定时任务是非常简单的,不用像spring ...

  8. opencv调整图像亮度对比度

    图像处理 图像变换就是找到一个函数,把原始图像矩阵经过函数处理后,转换为目标图像矩阵. 可以分为两种方式,即像素级别的变换和区域级别的变换 Point operators (pixel transfo ...

  9. 在Docker中启动Cloudera

    写在前面 记录一下,一个简单的cloudera处理平台的构建过程和一些基本组件的使用 前置说明 需要一台安装有Docker的机器 docker常用命令: docker ps docker ps -a ...

  10. CDH高可用hadoop集群性能配置

    1.HDFS的高可用配置 dfs.namenode.edits.dir (NameNode 编辑目录) : 写入 NameNode 编辑的本地文件系统上的目录.未指定将存放在namenode数据目录中 ...