Luogu-3878 [TJOI2010]分金币

Posted nianheng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu-3878 [TJOI2010]分金币相关的知识,希望对你有一定的参考价值。

这题和在我长郡考试时的一道题思路差不多...考虑折半搜索,预处理左半边选的方案所产生的数量差值(x)以及价值差值(y),把(y)扔到下标为(x)的set里面,然后在搜索右半边,每搜出一个状态,设他的数量差值为(a),价值差值(b),根据题意,要满足数量差值小于1,就要找左半边的状态来互补一下,很显然,如果(n)是偶数,数量差就一定是0,否则可以是正负1,所以要在(set[-a])(set[-a-1],set[-a+1])里二分找一个数(c)使他加(b)最小,答案去绝对值最小值就好了。注意下标要统一加(n),防止出现负值。

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef set<int> ST;
const int maxn=1<<15;
int t,n,a[maxn],ans;
ST st[110];
void ycl(){
    for(int i=0;i<=109;i++)
        st[i].clear();
    ans=0x7fffffff;
    int m=n/2,lim=1<<m;
    for(int i=0;i<lim;i++){
        int cnt=0,tot=0;
        for(int j=1,k=1;j<=m;j++,k<<=1)
            if(k&i) cnt++,tot+=a[j];
            else cnt--,tot-=a[j];
        st[cnt+n].insert(tot);
    }
}
int check(int x,int y){
    int ans=0x7fffffff;
    ST::iterator p=st[x].lower_bound(y);
    ST::iterator q=st[x].upper_bound(y);
    if(p!=st[x].end()) ans=min(ans,abs(*p-y));
    if(q!=st[x].end()) ans=min(ans,abs(*q-y));
    return ans;
}
void work(){
    int l=n/2;
    int m=n-l,lim=1<<m;
    for(int i=0;i<lim;i++){
        int cnt=0,tot=0;
        for(int j=1,k=1;j<=m;j++,k<<=1)
            if(k&i) cnt++,tot+=a[l+j];
            else cnt--,tot-=a[l+j];
        cnt=n-cnt;
        if(n%2)
            ans=min(ans,min(check(cnt-1,-tot),check(cnt+1,-tot)));
        else
            ans=min(ans,check(cnt,-tot));
    }
}
int main(){
//  freopen(".in","r",stdin);
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        ycl();
        work();
        printf("%d
",ans);
    }
    return 0;
}

以上是关于Luogu-3878 [TJOI2010]分金币的主要内容,如果未能解决你的问题,请参考以下文章

cogs 1430. [UVa 11300]分金币

分金币 [CQOI 2011] [BZOJ 3293]

bzoj3293[Cqoi2011]分金币

[BZOJ3293] [Cqoi2011] 分金币 (贪心)

蓝书第一章 例题3 分金币

P3879 [TJOI2010]阅读理解(Trie)