Official

D - チーム分け / Team Division Editorial by kyopro_friends Editorial for printing


この問題は DP により解くことができます。

\(S=\sum A_i\) とします。「 \(N\) 人から何人かを選んで、力の和を \(x\) にできるか?」を全ての \(0\leq x \leq S/2\) について求めることができれば、元の問題に答えることができます。

この問題は部分和問題なので、\(\mathrm{DP}[i][s]\) を「 \(i\) 番目の選手までから何人かを選んで、力の和を \(s\) にできるか?」と定めるDPにより解くことができます。しかし、このDPは \(O(NS)\) となるため、実行時間制限に間に合わせることは困難です。そこで、俗にbitset高速化と呼ばれる手法を用います。

\(\mathrm{DP'}[i]\) を、 \(s\) bit 目が \(\mathrm{DP}[i][s]\) となるような \(S\) bit の bit 列と定めます。

このとき、DP の遷移は \(\mathrm{DP'}[i+1]=\mathrm{DP'}[i] \mathbin{|} (\mathrm{DP'}[i] \ll A_{i+1})\) となります(\(|\) は bitwise or、 \(\ll\) はビットシフト)。これにより ワードサイズを \(w\) として、この遷移は \(O(S/w)\) で行うことができ、全体で \(O(NS/w)\) で答えを求めることができます。

このような操作を可能とする bit 列の表現はプログラミング言語ごとに異なります。C++では bitset と呼ばれるデータ構造を用います。Pythonでは単に int を用いることができます。

実装例 (C++)

#include<bits/stdc++.h>
using namespace std;

int main(){
  int n;
  cin >> n;
  bitset<100010> dp;
  dp[0]=true;
  int s = 0;
  for(int i=0; i<n; i++){
    int a;
    cin >> a;
    s += a;
    dp |= dp << a;
  }

  for(int i=s/2; i>=0; i--){
    if(dp[i]){
      cout << i << endl;
      return 0;
    }
  }
}

実装例 (Python)

N = int(input())
dp = 1
S = 0
for _ in range(N):
  a = int(input())
  S += a
  dp |= dp << a

for i in range(S//2, -1, -1):
  if dp & (1<<i):
    print(i)
    exit()

posted:
last update: