题目
题目描述
现在有 nn 枚金币,它们可能会有不同的价值,第 ii 枚金币的价值为 v_iv
i
。
现在要把它们分成两部分,要求这两部分金币数目之差不超过 11,问这样分成的两部分金币的价值之差最小是多少?
输入格式
本题单个测试点内有多组测试数据。
输入的第一行是一个正整数 TT,表示该测试点内数据组数。
对于每组测试数据的格式为:
每组测试数据占两行。
第一行是一个整数 nn,表示金币的个数。
第二行有 nn 个整数,第 ii 个整数表示第 ii 个金币的价值 v_iv
i
。
输出格式
对于每组数据输出一行一个整数表示答案。
输入输出样例
输入 #1复制
2
3
2 2 4
4
1 2 3 6
输出 #1复制
0
2
说明/提示
数据规模与约定
对 30%30% 的数据,保证 1 \leq v_i \leq 10001≤v
i
≤1000
对于 100%100% 的数据,保证 1 \leq T \leq 201≤T≤20,1 \leq n \leq 301≤n≤30,1 \leq v_i \leq 2^{30}1≤v
i
≤2
30
。
思路
模拟退火
因为本题是要求平分,所以我们先锁边乱搞一下,分成两个组别,
每次在把一个组和另一个组的金币交换
显然,这一个过程可以用模拟退火来实现
代码
#include<bits/stdc++.h>
#define inf 2147483647
#define re register
using namespace std;
int n,ans=inf,a[1005];
int get()
{
int sum1=0,sum2=0;
for (re int i=1;i<=(n+1)/2;i++)
sum1+=a[i];
for (re int i=(n+1)/2+1;i<=n;i++)
sum2+=a[i];
return abs(sum1-sum2);
}
void SA()
{
double beginT=5000,endT=1e-10,changeT=0.9112;
for (re double T=beginT;T>endT;T*=changeT)
{
int x=rand()%n+1, y=rand()%n+1;
swap(a[x],a[y]);
int sum=get();
if (sum<ans)
ans=sum;
else
if (exp((ans-sum)/T)<(double(rand())/RAND_MAX))
swap(a[x],a[y]);
}
}
int main()
{
srand(rand());
int T;
cin>>T;
while (T--)
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
int ctrl=1000;
while(ctrl--)
SA();
cout<<ans<<endl;
ans=inf;
}
}