H - Bad Juice 解説
by
leaf1415
呼ぶ人数が \(M\) 人のとき、翌日に各友人がお腹を壊したかの情報 \(S\) が取りえるのは高々 \(2^M\) 通りなので、 \(N\) 通り考えられる腐ったジュースの番号を \(S\) の情報から一意に識別するには \(2^M \geq N\) 、つまり、\(M \geq \left\lceil \log_2 N \right\rceil\) が必要です。
反対に、呼ぶ人数は \(b \coloneqq \left\lceil \log_2 N\right\rceil\) 人で十分です。実際に、以下に述べる手順によって \(b\) 人の友人を利用して腐ったジュースを特定できます。
便宜上、ジュースの番号と友人の番号を \(0\) から起算し、それぞれ \(0, 1, 2, \ldots, N-1\) と \(0, 1, 2, \ldots, b-1\) とします。 このとき、\(2^b-1 \geq N-1\) なので、すべてのジュースの番号は \(b\) 桁の二進数で表現できます。 その各桁を最下桁から順に \(0, 1, 2, \ldots, b-1\) ビット目と番号づけます。 そして、下記の通りに各友人に飲ませるジュースの組み合わせを決定します。
友人 \(i = 0, 1, 2, \ldots, b-1\) とジュース \(j = 0, 1, 2, \ldots, N-1\) の各組 \((i, j)\) について、 \(i\) 番目の友人には、ジュースの番号 \(j\) の二進数表現における \(i\) ビット目が立っているとき、かつ、そのときに限り \(j\) 番目のジュースを飲ませる。
このとき、腐ったジュースの番号 \(X\) の二進数表現の \(0 ,1, 2, \ldots, b-1\) 桁目と、文字列 \(S\) の \(0, 1, 2, \ldots, b-1\) 文字目(先頭の文字を \(0\) 文字目とする)が一致し、\(X\) を \(S\) から一意に特定できます。
以下に、C++ 言語による本問題の正解例を記載します。
#include <iostream>
#include <vector>
using namespace std;
int main(void){
int n;
cin >> n;
int b = 0;
while((1<<b) < n) b++;
cout << b << endl;
for(int i = 0; i < b; i++){
vector<int> avec;
for(int j = 0; j < n; j++) if(j & (1<<i)) avec.push_back(j+1);
cout << avec.size() << " ";
for(auto a : avec) cout << a << " ";
cout << endl;
}
string s;
cin >> s;
int ans = 0;
for(int i = 0; i < b; i++) if(s[i] == '1') ans |= 1<<i;
cout << ans+1 << endl;
return 0;
}
投稿日時:
最終更新: