【CF226C】Anniversary

题目

题目描述
现在有 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;
    }
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值