[Luogu3878] [TJOI2010]分金币

题目描述

现在有n枚金币,它们可能会有不同的价值,现在要把它们分成两部分,要求这两部分金币数目之差不超过1,问这样分成的两部分金币的价值之差最小是多少?

输入输出格式

输入格式:

每个输入文件中包含多组测试数据,输入文件的第一行是一个正整数T,用来说明文件中有多少组测试数据。接下来将依次给出所有测试数据的描述,每组测试数据的第一行是一个正整数n,表示共有n枚金币。第二行有n个正整数vi,分别给出每一枚金币的价值。

输出格式:

对每一组输入数据,输出一个非负整数,表示分成的两部分金币的价值差别的最小值。

输入输出样例

输入样例#1: 复制
2
3
2 2 4
4
1 2 3 6
输出样例#1: 复制
0
2

说明

对30%的数据,1 ≤ vi ≤ 1000

对100%的数据,1 ≤ n ≤ 30,1 ≤ vi ≤ 230,T ≤ 20

每个测试点时限1秒

注意:对于使用C/C++语言的选手,若需使用64位整型数,应声明为long long,若需使用scanf()/printf()/fscanf()/fprintf()等系列函数,应配合使用"%lld"标记符进行long long类型的输入输出。


看到这题的数据范围就满脑子想乱搞...

我感觉我的noip完了。
我可能也就只会这种乱搞题了,

就这样乱搞,实测可以AC。


#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;
}

猜你喜欢

转载自www.cnblogs.com/BriMon/p/9615451.html