Codeforces edu 6. D Professor GukiZ and Two Arrays 二分搜索 枚举

题目

题目链接:http://codeforces.com/contest/620/problem/D

题目来源:Educational Codeforces Round 6

简要题意:对于两个数组,在两数组间最多交换两次,求两数组之和的差的绝对值最小值。

题解

这题也属于理解起来简单,但是做起来比较棘手的题目,好题。

先分类,不交换的话可线性求出,交换 1 次可以平方枚举

比较棘手的就是2次的情况了。

首先我想到的一个办法是将所有的差塞入一个map然后利用原始数组和的差及枚举的一个差来二分。

但是这样做的问题是由于是要按照顺序来的,假如使用了相同的位置位置上的数已经发生了变化就会弄出来错误的答案了。

进一步考虑,假如换了相同的位置实际上就等价于换了一次,那先做 1 再做2不就能够不会更小就不会更新了么?然后就会继续坑了,因为这是假设两个差是正确的情况下,实际上map的两个差已经是错误的值了,错误的值可能更小,就会出问题了。

实际上既然必然选择是两个不同的,可以枚举 a 中两数的组合塞进map然后枚举b中的两数来二分map

这样就可以避免两次选到同一位置的问题了,比赛时思维定势没能做出。

代码

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <stack>
#include <queue>
#include <string>
#include <vector>
#include <set>
#include <map>

#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define sz(x) ((int)(x).size())
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
LL powmod(LL a,LL b, LL MOD) {LL res=1;a%=MOD;for(;b;b>>=1){if(b&1)res=res*a%MOD;a=a*a%MOD;}return res;}
// head
const int N = 2005;

int a[N];
int b[N];

struct Result {
    int cnt;
    PII pr[2];
    LL ans;

    void printAns() {
        printf("%I64d\n%d\n", ans, cnt);
        for (int i = 0; i < cnt; i++) {
            printf("%d %d\n", pr[i].fi, pr[i].se);
        }
    }
};

map<LL, PII> ma;
int main()
{
    int n, m;
    LL suma = 0, sumb = 0;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", a+i);
        suma += a[i];
    }
    scanf("%d", &m);
    for (int i = 0; i < m; i++) {
        scanf("%d", b+i);
        sumb += b[i];
    }

    Result temp, ans;
    ans.cnt = 0;
    ans.ans = abs(suma-sumb);

    LL tempsa, tempsb, tempans;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            tempsa = suma - a[i] + b[j];
            tempsb = sumb - b[j] + a[i];
            tempans = abs(tempsa-tempsb);
            if (abs(tempsa-tempsb) < ans.ans) {
                ans.ans = abs(tempsa-tempsb);
                ans.cnt = 1;
                ans.pr[0] = mp(i+1, j+1);
            }
        }
    }

  for (int i = 0; i < n; i++) {
        for (int j = i+1; j < n; j++) {
            ma[2LL * (a[i]+a[j])] = mp(i+1, j+1);
        }
  }

  for (int i = 0; i < m; i++) {
        for (int j = i+1; j < m; j++) {
            LL x = suma-sumb+2LL*(b[i]+b[j]);
            map<LL, PII>::iterator it = ma.upper_bound(x);
            if(it != ma.end() && -x + it->fi < ans.ans) {
                ans.ans = -x + it->fi;
                ans.cnt = 2;
                ans.pr[0] = mp(it->se.fi, i+1);
                ans.pr[1] = mp(it->se.se, j+1);
            }
            if(it != ma.begin()) {
                it--;
                if(x - it->fi < ans.ans) {
                    ans.ans = x - it->fi;
                    ans.cnt = 2;
                    ans.pr[0] = mp(it->se.fi, i+1);
                    ans.pr[1] = mp(it->se.se, j+1);
                }
            }
        }
  }
    ans.printAns();

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值