1. 程式人生 > >620D Professor GukiZ and Two Arrays 二分 | 雙指標 STL

620D Professor GukiZ and Two Arrays 二分 | 雙指標 STL

題目連結

題意:

給定N≤2×103的兩個序列,給定0≤k≤2次交換2個序列中一個數的操作,使得|suma−sumb|最小

思路:

考慮N最大為2e3,所以考慮對交換0次或1次的我們可以直接暴力來求,列舉哪兩個數交換,複雜度 O(N2)

       1.交換一次 零次

設sa-sb為s,假設交換a[i]和b[j]兩個元素那麼 sa’ = s-a[i]+b[j] ,sb' = s-b[j]+a[i]。 s'=|sa-sb| =|s-2*a[i]+2*b[j]|.

2.交換兩次.根據上面的方法,我們假設交換兩次時兩個數分別為 a[i1],a[i2],b[j1],b[j2]。

那麼新的s' = s-2*(a[i1]+a[i2])+2*(b[j1]+b[j2])。為了讓s'儘可能小,s'最小為0,那麼有2*(b[j1]+b[j2])=2*(a[i1]+a[i2])-s.  那麼我們選取的b[j1]+b[j2]的可能值有正好等於右面,剛大於右面,剛小於右面(因最後結果要絕對值)。

所以我們可以預處理任意a【i1】+a【i2】的和,然後二分去找b【j1】+b【j2】 複雜度O(N2LOGN)

#include<bits/stdc++.h>
#define mk make_pair
using namespace std;
typedef long long ll;
const ll inf = 1e19;
const int maxn = 2e3+10;

ll a[maxn],b[maxn];
pair<ll,pair<int,int> > ansb[maxn*maxn];
pair<int,int>ans,ans1,ans2;
int main()
{
	int n,m;
	scanf("%d",&n);
		int cnt = 0;
		ll s = 0;
		for(int i = 1;i <=n ;i++)
		{
			scanf("%lld",&a[i]);
			s += a[i]; 
		}
		scanf("%d",&m);
		for(int i = 1;i <= m ;i++)
		{
			scanf("%lld",&b[i]);
			s -= b[i];
		}
		ll s2 = inf;
		for(int i = 1;i <= n;i++)
		{
			for(int j = 1;j <= m;j++)
			{
				ll tmp = abs(s - 2*a[i] +2*b[j]);
				if(s2 > tmp)
				{
					s2 = tmp;
					ans =mk(i,j);
				}
			}
		}
		for(int i = 1;i <= m;i++)
			for(int j = i + 1;j <= m;j++)
			ansb[++cnt] = mk(2*(b[i] + b[j]),mk(i,j));
		sort(ansb+1,ansb+cnt+1);
		ll s3 = inf;
		for(int i = 1;i <= n;i++)
			for(int j = i + 1;j <= n;j++)
			{
				ll tmp = 2*a[i] + 2*a[j] - s;
				int t = lower_bound(ansb+1,ansb+1+cnt,mk(tmp,mk(0,0))) - ansb;
				for(int k = max(1,t-1);k<=min(cnt,t+1);k++)//小技巧
				{
					ll now = abs(ansb[k].first - tmp);
					if(now < s3)
					{
						s3 = now;
						ans1 = mk(i,ansb[k].second.first);
						ans2 = mk(j,ansb[k].second.second);
					 } 
				}
				
			}
		 ll res = min(min(abs(s),abs(s2)),abs(s3));
		 printf("%lld\n",res);
		 if(res == abs(s)) puts("0");
		 else if(res == abs(s2))
		 {
		 	puts("1");
		 	printf("%d %d\n",ans.first,ans.second);
		 }
		 else
		 {
		 	puts("2");
		 	printf("%d %d\n%d %d\n",ans1.first,ans1.second,ans2.first,ans2.second);
		 }
	return 0;
}

PS:當然這個題還有更好的做法,因為觀察到因為s一定的,隨著a[i1]+a[i2]只的增大,b[j1]+b[j2]只增不減,根據這個性質可以採用雙指標.預處理任意a[i1]+a[i2]和b[j1]+b[j2] 的和,然後使他們各自有序,然後雙指標掃描,還是尋找上述三種情況的,

這樣複雜度為n2,可以降個LOG .這裡不給出程式碼了.