SRM 616 DIV1 Middle - ColorfulCoins (復習×)
問題
硬貨が複数種類存在し、1枚当たりの価値value[i]があらかじめわかっている。
それぞれの硬貨は異なる色をしている。
またATMが存在し、好きな回数だけ好きな金額を引き出すことができる。
引きだされる金額は、もっとも硬貨が少ないように引きだされる。
このとき、すべての硬貨の色の価値を判定するために必要な最小の引き出し回数を求める。
考えたこと
サンプルより1度に引き出せないときは、ある硬貨の価値の半分の硬貨があり、かつ倍の価値の硬貨があるときに存在しそう。
また各硬貨について他の硬貨にならないように一度に引き出せる枚数は、その次の硬貨の価値から割りだせそう。
では1度の引き出しについて枚数が異なるように選び、その組み合わせの最大化で解けるか。
サンプルと照らし合わせると合わないので、もっと最小となる考え方がありそう。
ここでEditorialを読む。
基本的には、2進数で表せる数とビット数の関係を用いる。
全てのコインが2枚まで引き出せると、2回の判定で9通りまで判定できる。
(例)引き出されたコインの枚数
1回目:0 0 0 1 1 1 2 2 2
2回目:0 1 2 0 1 2 0 1 2
ただしすべてのコインが2回引き出せなくとも、1枚まで引き出せるコインが4枚までなら
上記の判定に含むことができる。
4枚というのは、2回の引き出しにおける0と1の組み合わせ数。
つまり引き出す回数がn回とすると、各コインの引き出せる最大の数xのコインがx^2までで
あればよい。
ただしExampleから、どのコインも少なくても1回は引き出さないといけない。
これは引き出せる最大のコインの枚数でソートすると何枚目かの情報もわかるので、
各配列のi番目にある最大数xのコインについて x^2>=i+2であるような最小のnを求めればよい。
最後に、long longの割り算でint型に代入しようとするとintを超える、またはマイナスになるケースがあるので同じ型で取り扱う必要がある。
コード
class ColorfulCoins {
public: int minQueries(vector<long long> values) {
vector<long long> num;
int n=values.size();
FORE(i,0,n-1)num.push_back( min(100LL,(long long)(values[i+1]/values[i])) );
sort(all(num));
FORE(count,1,7){
int ok=1;
FORE(i,0,num.size()){
int x=1;
FORE(j,0,count)x*=(num[i]);
if(x<i+2)ok=0;
}
if(ok)return count;
}
return 6;
}
};
public: int minQueries(vector<long long> values) {
vector<long long> num;
int n=values.size();
FORE(i,0,n-1)num.push_back( min(100LL,(long long)(values[i+1]/values[i])) );
sort(all(num));
FORE(count,1,7){
int ok=1;
FORE(i,0,num.size()){
int x=1;
FORE(j,0,count)x*=(num[i]);
if(x<i+2)ok=0;
}
if(ok)return count;
}
return 6;
}
};