AtCoder Beginner Contest 181 E - Transformable Teacher (贪心,二分)
-
题意:有一长度为奇数\(n\)的数组\(a\),和长度为\(m\)的数组\(b\),现要求从\(b\)中选择一个数放到\(a\)中,并将\(a\)分成\((n+1)/2\)个数对,求最小的所有数对差的和.
-
题解:我们从\(b\)中选一个数出来,只和\(a\)中的一个元素配对,剩下的依然是\(a\)中\(n-1\)个数两两配对,所以我们可以先求个前缀/后缀和,然后枚举\(b\)在\(a\)中二分查找和\(b_i\)最近的数,但是会有个小问题,加入我们二分查找的位置是偶数,此时并不能完全配对,但是没关系,我们让偶数位置的前一个数与\(b_i\)配对,结果是一样的,比如\(a,b,c,d,e\),我们找与\(x\)最近的数,找到了\(b\),于是我们让\(b\)的前一个数\(a\)与其配对,有:\(a-x+b-c+d-e\),这个式子与\(b-x+a-c+d-e\)是等价的,所以这样就可以保证我们的贪心是对的(这个操作看了我半天),每次枚举维护一个最小值即可.
-
代码:
int n,m; int h[N],w[N]; ll L[N],R[N]; ll ans=1e18; int main() { ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); cin>>n>>m; for(int i=1;i<=n;++i) cin>>h[i]; for(int i=1;i<=m;++i) cin>>w[i]; sort(h+1,h+1+n); for(int i=2;i<=n;i+=2) L[i]=L[i-2]+abs(h[i]-h[i-1]); for(int i=n-1;i>=1;i-=2) R[i]=R[i+2]+abs(h[i]-h[i+1]); for(int i=1;i<=m;++i){ int pos=lower_bound(h+1,h+1+n,w[i])-h; if(pos%2==0) pos--; ans=min(ans,L[pos-1]+R[pos+1]+abs(h[pos]-w[i])); } cout<<ans<<endl; return 0; }
𝓐𝓬𝓱𝓲𝓮𝓿𝓮𝓶𝓮𝓷𝓽 𝓹𝓻𝓸𝓿𝓲𝓭𝓮𝓼 𝓽𝓱𝓮 𝓸𝓷𝓵𝔂 𝓻𝓮𝓪𝓵
𝓹𝓵𝓮𝓪𝓼𝓾𝓻𝓮 𝓲𝓷 𝓵𝓲𝓯𝓮