ラベル ビット の投稿を表示しています。 すべての投稿を表示
ラベル ビット の投稿を表示しています。 すべての投稿を表示

2014 TCO Round 3B - IntoTheMatrix

2014 TCO Round 3B - IntoTheMatrix

問題


http://community.topcoder.com/stat?c=problem_statement&pm=13066&rd=16058

N種類のピルがあり、どれがマジックピルかを判別したい。
判別するのに友人に協力してもらうことができ、マジックピルを飲んだ友人は消えてしまう。
協力してもらうターン数turnsが与えられた時、最低限必要な友人の数を求める。

解き方


・問題文から、ターンと友人の数により判別できるピルの数を列挙してみる
・問題文の誘導からか、なんとなくはまってしまう。

・他の人のコードを読む

・要は、人数とターンにより表わされる状態数が判別できるピルの数
・例えば1ターンでA,Bがいるときは、{ABが残る}、{Aだけ残る}、{Bだけ残る}、{両方消える}の4通り
・友人の数を固定すると、1人のとき1ターンで2通り、2ターンで3通り・・・Tターンで(T+1)通り

・よって、友人の数を0から増やしていき、(T+1)^友人の数がN以上となるところが答え

コード


using namespace std;

#define all(c) (c).begin(),(c).end()
#define FORE(i,d,e) for(int i=d;i<e;i++)
#define FOR(i,s,e) for (int i = int(s); i != int(e); i++)
#define FORIT(i,c) for (typeof((c).begin()) i = (c).begin(); i != (c).end(); i++)
#define ISEQ(c) (c).begin(), (c).end()

class IntoTheMatrix {

public: int takePills(int turns, int N) {
int F=0;
long long x=1;

while(x<(long long)N){
x*=(turns+1);
F++;
}

return F;
}

};

SRM 632 DIV1 Easy - PotentialArithmeticSequence (×)

SRM 632 DIV1 Easy - PotentialArithmeticSequence (×)

問題


http://community.topcoder.com/stat?c=problem_statement&pm=13389&rd=16075

数字の数列があり、その数字を2進数にしたときの末尾の0の数のリストが与えられる。
このとき、与えられた数列のうち1ずつ増加するサブ数列の数を求める。

解き方


数列に法則がないか列挙してみる。

すると、以下のように再帰の法則があるとわかる。
1が現れるとき:010
2が現れるとき:0102010
3が現れるとき:010201030102010

よって、与えられた数列の全てのサブ数列に対し、以下の法則が成り立つか
再帰を用いてチェックすればよい。

コード


using namespace std;

#define all(c) (c).begin(),(c).end()
#define FORE(i,d,e) for(int i=d;i<e;i++)
#define FOR(i,s,e) for (int i = int(s); i != int(e); i++)
#define FORIT(i,c) for (typeof((c).begin()) i = (c).begin(); i != (c).end(); i++)
#define ISEQ(c) (c).begin(), (c).end()

class PotentialArithmeticSequence {

public:

bool can(vector<int> d){
if(d.size()==1)return true;
int p= d[0]==0 ? 0 : 1;
int n=d.size();

vector<int> next;
FORE(i,0,n){
if(i%2==p){
if(d[i]!=0)return false;
}
else{
if(d[i]==0)return false;
next.push_back(d[i]-1);
}
}
return can(next);
}

int numberOfSubsequences(vector<int> d) {
int ret=0;
int n=d.size();

FORE(i,0,n)FORE(j,i,n){
vector<int> cur;
FORE(k,i,j+1)cur.push_back(d[k]);
if(can(cur))ret++;
}
return ret;
}

};

SRM 439 DIV1 Easy - PouringWater

SRM 439 DIV1 Easy - PouringWater

問題


http://community.topcoder.com/stat?c=problem_statement&pm=10408&rd=13747

N個のボトルがあり、最初は1Lだけ水が入っている。
ここで同じ量だけ水が入っているボトルを選んで、一つのボトルに水をまとめることができる。
このとき、水が入っているボトルをK個以下にしたい。
ただし現在のボトル数ではK個以下にできない場合は、ボトルを追加で買うことができる。

このとき、水が入っているボトルをK個以下にしたいときに、追加で必要なボトルの最小数を求める。

解き方


ぱっと見た感じだと解法が思い浮かばないので、ボトル数の計算を式に落とせないか考えてみる。
ここで、1個、2個、4個、8個・・・のボトルは一つにまとめられることから
2^K個のボトルは1つにまとめることができる。

次に例をあげてみると、
15=2^3++2^2+2^1+1のように変換することができる。

上記の式を眺めてみると、ボトルの数は2進数に変換したときの1の数と等しい
ことがわかる。

次にボトルを減らしたいときに買うべきボトルの数を、先の例を使って考える。
15=1111であり、ボトルを1つ買うと
10000、つまりボトルが1つになる。

つまり、右から走査し1が存在した時はその数を足し、
ボトル数がK個以下になるまで繰り返してあげればよい。

コード


#define all(c) (c).begin(),(c).end()
#define FORE(i,d,e) for(int i=d;i<e;i++)
#define FOR(i,s,e) for (int i = int(s); i != int(e); i++)
#define FORIT(i,c) for (typeof((c).begin()) i = (c).begin(); i != (c).end(); i++)
#define ISEQ(c) (c).begin(), (c).end()

class PouringWater {

public:

int calc(int x){
int ret=0;
while(x>0){
ret+=x%2;
x/=2;
}
return ret;
}

int getMinBottles(int N, int K) {
int ret=0;

while(calc(N)>K){
for(int i=0;i<32;i++){
if(N&(1<<i)){
ret+=(1<<i);
N+=(1<<i);
break;
}
}
}

return ret;
}

};

SRM 596 DIV1 Easy - IncrementAndDoubling

SRM 596 DIV1 Easy - IncrementAndDoubling

問題


http://community.topcoder.com/stat?c=problem_statement&pm=12790&rd=15708

・求める整数の集合が与えられる。
・オペレーションはひとつの整数を1足すか全ての整数を2かけるかの2種類。
・各整数は0からスタートし、オペレーションを繰り返して求める整数の集合にする。
・このとき、必要な最小オペレーション回数を求める。

解き方


全体の積の回数を正しく求められるかがポイント。
0にかけても0になることを利用できることがわかれば、
各整数の中で最大の積の回数が配列全体の積の回数と等しいことが導ける。

各整数の積と和の回数は2進数で考えることで求められる。
積の回数は2進数にしたときの桁数ー1であり、
和の回数は2進数にしたときの1の個数になる。

コード


class IncrementAndDoubling {

public: int getMin(vector<int> desiredArray) {
int m=1;
int add=0;

FORE(i,0,desiredArray.size()){
int tmp=0;
while(desiredArray[i]>0){
tmp++;
add+=desiredArray[i]%2;
desiredArray[i]/=2;
}
m=max(m,tmp);
}

return m-1+add;
}

};

using namespace std;

#define all(c) (c).begin(),(c).end()
#define FORE(i,d,e) for(int i=d;i<e;i++)
#define FOR(i,s,e) for (int i = int(s); i != int(e); i++)
#define FORIT(i,c) for (typeof((c).begin()) i = (c).begin(); i != (c).end(); i++)

class IncrementAndDoubling {

public: int getMin(vector<int> desiredArray) {
int n=desiredArray.size();

int m=0,add=0;
FORE(i,0,n){
int cur=desiredArray[i],cnt=0;
while(cur>0){
if(cur%2)cur--,add++;
else{
cur/=2;
cnt++;
}
}
m=max(m,cnt);
}

return add+m;
}

};

SRM 519 DIV1 Easy - BinaryCards

SRM 519 DIV1 Easy - BinaryCards

問題


http://community.topcoder.com/stat?c=problem_statement&pm=11552&rd=14544

正の整数AとBが与えられる。
AからBを得るには、2進数で表わされたAから各ビットを返していく。
この操作を行ったとき、途中で現れる最大の数を求める。

解き方


A,Bは最大10^18のため単純なシミュレーションでは解くことができない。
そのため、計算量を削減する法則がないか考える。

2進数にしてみて、最初と最後の状態までのプロセスの変化が答えに関連しないか考えてみる。
いくつか例を出してみると、
一度も触ったことがないビットは固定しなければいけないが
「AからBを得るときに1度でもひっくり返したビット以降はすべて1」にできることがわかる。


コード


class BinaryCards {

public: long long largestNumber(long long A, long long B) {

for(long long i=63;i>=0;i--){
long long x=1LL<<i;
if((A&x)!=(B&x))return A | ( (1LL<<(i+1))-1);
}
return A;
}

};