时空限制 1000ms / 128MB
题目描述
现在有n枚金币,它们可能会有不同的价值,现在要把它们分成两部分,要求这两部分金币数目之差不超过1,问这样分成的两部分金币的价值之差最小是多少?
输入格式:
每个输入文件中包含多组测试数据,输入文件的第一行是一个正整数T,用来说明文件中有多少组测试数据。接下来将依次给出所有测试数据的描述,每组测试数据的第一行是一个正整数n,表示共有n枚金币。第二行有n个正整数vi,分别给出每一枚金币的价值。
输出格式:
对每一组输入数据,输出一个非负整数,表示分成的两部分金币的价值差别的最小值。
说明
对30%的数据,1 ≤ vi ≤ 1000
对100%的数据,1 ≤ n ≤ 30,1 ≤ vi ≤ 230,T ≤ 20
题目分析
看题解好像正解是折半状压/搜索?? 不管它,退火能写就行
模拟退火不断随机新序列,每次把新序列从中间分成两部分比较答案就行
#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
#include<ctime>
using namespace std;
typedef long long lt;
typedef double dd;
#define T 1000
#define eps 1e-8
#define delta 0.997
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const int inf=1e9;
const int maxn=50;
int Q,n;
int a[maxn],ans;
int DP()
{
int res1=0,res2=0;
for(int i=1;i<=n;++i)
if(i<=(n+1)/2) res1+=a[i];
else res2+=a[i];
return abs(res1-res2);
}
void SA()
{
dd t=T;
while(t>eps)
{
int x=0,y=0;
while(x==y) x=rand()%n+1,y=rand()%n+1;
swap(a[x],a[y]);
int tt=DP();
int dE=ans-tt;
if(dE>=0) ans=tt;
else if( exp(dE/t) > (dd)rand()/(dd)RAND_MAX ) ;
else swap(a[x],a[y]);
t*=delta;
}
}
int main()
{
srand((unsigned)time(NULL));
Q=read();
while(Q--)
{
n=read();
for(int i=1;i<=n;++i) a[i]=read();
if(n==1){ printf("%d\n",a[1]); continue;}
ans=DP();
for(int i=1;i<=50;++i) SA();
printf("%d\n",ans);
}
return 0;
}