Luogu-3878 [TJOI2010]分金幣
阿新 • • 發佈:2018-11-30
註意 printf urn per 偶數 ... mat cpp work
這題和在我長郡考試時的一道題思路差不多...考慮折半搜索,預處理左半邊選的方案所產生的數量差值\(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\n",ans); } return 0; }
Luogu-3878 [TJOI2010]分金幣