Official

D - 花壇の花選び / Choosing Flowers for the Flower Bed Editorial by kyopro_friends


各花を植えるかどうか、 \(2^N\) 通りを全探索すればよいです。

以下では俗にbit全探索と呼ばれる実装上の工夫について説明します。

\(\{0,1,\dots,N-1\}\) の部分集合 \(S\) に対し、 \(X_S\)\(\sum_{i\in S}2^i\) と定めます。この対応では、\(i\)\(S\) に含まれることと、\(X_S\)\(i\) - bit目が \(1\) であることは同値になります。
またこの対応は \(\{0,1,\dots,N-1\}\) の部分集合と \(0\) 以上 \(2^N-1\) 以下の整数の間の全単射となります。
この事実を用いて、\(\{0,1,\dots,N-1\}\) の部分集合全てを調べることを、\(0\) 以上 \(2^N-1\) 以下の整数に関する通常の for 文で行うことができ、集合に対する操作を bit 演算で行うことができます。

実装例 (C++)

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

int main(){
  int n, k, m;
  cin >> n >> k >> m;
  vector<int>s(n);
  for(int i=0; i<n; i++) cin >> s[i];
  vector<array<int,3>>data(m);
  for(int i=0; i<m; i++){
    cin >> data[i][0] >> data[i][1] >> data[i][2];
    data[i][0]--;
  }

  int ans = 0;
  for(int x=0; x<1<<n; x++){
    if(__builtin_popcount(x) > k){
      continue;
    }
    int crr = 0;
    for(int i=0; i<n; i++){
      if(x & (1<<i)){
        crr += s[i];
      }
    }
    for(int i=0; i<m; i++){
      auto[l, r, p] = data[i];
      if(x & ( (1<<r) - (1<<l) ) ){
        crr += p;
      }
    }
    ans = max(ans, crr);
  }
  cout << ans << endl;
}

実装例 (Python)

N, K, M = map(int, input().split())
S = list(map(int, input().split()))
data = []
for _ in range(M):
  L, R, P = map(int, input().split())
  L -= 1
  data.append((L, R, P))

ans = 0
for x in range(1<<N):
  if x.bit_count() > K:
    continue
  crr = 0
  for i, s in enumerate(S):
    if x & (1<<i):
      crr += s
  for l, r, p in data:
    if x & ( (1<<r) - (1<<l) ):
      crr += p
  ans = max(ans, crr)

print(ans)

posted:
last update: