けんちょんの競プロ精進記録

競プロの精進記録や小ネタを書いていきます

AtCoder ABC 100 D - Patisserie ABC (400 点)

ABC 100 D - Patisserie ABC

問題概要

整数 3 つ組 (xi, yi, zi) が N 個与えられる。
このうちの M 個選んで、

(x の選んだ M 個の総和の絶対値) + (y の選んだ M 個の総和の絶対値) + (z の選んだ M 個の総和の絶対値)

が最大になるようにせよ。

制約

  • 1 <= N <= 1000
  • -1010 <= xi, yi, zi <= 1010

解法

一瞬部分和問題的な DP をしたくなるけれど、x, y, z の制約が 109 とかなので無理そうだという感じになる。

絶対値をとるという操作は、「そのまま」と「-1 倍したもの」の大きい方を選ぶ操作に他ならない。したがって選んだ M 個の最適解は

  • x についての M 個の総和の、「そのまま」or「-1 倍する」 の大きい方
  • y についての M 個の総和の、「そのまま」or「-1 倍する」 の大きい方
  • z についての M 個の総和の、「そのまま」or「-1 倍する」 の大きい方

を足した形になっている。

ここで、それぞれの「そのまま」or「-1 倍する」を決め打ちして例えば

  • x については M 個の総和の、「そのまま」
  • y については M 個の総和の、「-1 倍」
  • z については M 個の総和の、「そのまま」

が最適解だったと仮定すると、最適解は

  • x[i] - y[i] + z[i] が大きい順に M 個選んで足したもの

になる。同様に、x, y, z それぞれについての「そのまま」「-1 倍する」のパターンを 23 = 8 通り試して、一番大きいものをとればよい。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int N, M;

int main() {
    cin >> N >> M;
    vector<long long> a[3];
    for (int i = 0; i < N; ++i) {
        for (int j = 0; j < 3; ++j) {
            long long num; cin >> num;
            a[j].push_back(num);
        }
    }
    long long res = 0;
    for (int bit = 0; bit < (1<<3); ++bit) {
        vector<long long> b;
        for (int i = 0; i < N; ++i) {
            long long tmp = 0;
            for (int j = 0; j < 3; ++j) {
                if (bit & (1<<j)) tmp += a[j][i];
                else tmp -= a[j][i];
            }
            b.push_back(tmp);
        }
        sort(b.begin(), b.end(), greater<long long>());
        long long sum = 0;
        for (int i = 0; i < M; ++i) {
            sum += b[i];
        }
        res = max(res, sum);
    }
    cout << res << endl;
}