Single Round Match 437 (Div2/1000point)

久しぶりに時間が十分余ったので1000点問題にも挑戦。「1からスタートして与えられた整数nまで何回の操作でたどり着けるかこたえなさい。やっていい操作は

  • 1増やす
  • 1減らす(元の値が1より大きい場合のみ)
  • 累乗する(たとえば元の値が2なら4とか8とか16に変える)

の3つ」


単純に計算すると当然3の累乗で可能性が膨らんで行って発散するので「すでに出た数字は探索しない(最短ではないから)」「ゴールを超えた数字はdecrementで戻ってくるしかないので、powerはギリギリ超えるところまで」「ゴールを超えている数はインクリメントしない」という枝刈りを入れたのだけど、それでもまだ枝刈りが足りなくて、10分前から大急ぎでdistanceって変数を入れて「一番ゴール近い数とゴールの距離よりもゴールより上にはなれている数は最短になり得ないので探索しない」「終盤でpowerが使われなくなったらあとはincrementやdecrementでにじり寄るだけなので+distanceして終了」ってやったけど手元でreal 0m4.153s。タイムアウトで全然ダメ。

  int count(long long goal) {
    typedef unsigned long long ULL;
    const ULL MAX_ULL = 18446744073709551615ULL;
    set<ULL> cur;
    cur.insert(1);
    set<ULL> next;
    set<ULL> used;
    ULL distance = goal - 1;
    typedef set<ULL>::iterator siter;
    int result = 0;
    if(goal == 1) return 0;
    while(true){
      used.insert(cur.begin(), cur.end());

      result++;
      bool skip = true;
      for(siter iter=cur.begin(), end=cur.end(); iter != end; ++iter){
	ULL n = *iter;
	ULL x = n;
	if(n == 1) skip = false;
	if(n != 1){
	  // power
	  while(MAX_ULL / n >= x && x < ULL(goal)){
	    x *= n;
	    if(x == ULL(goal)) return result;
	    if(used.find(x) == used.end() && x < ULL(goal) + distance){
	      next.insert(x);
	      skip = false;
	      if(goal - x > 0 && goal - x < distance){
		distance = goal - x;
	      }
	      if(x - goal > 0 && x - goal < distance){
		distance = x - goal;
	      }

	    }
	  }
	  
	  // decrement
	  x = n - 1;
	  if(used.find(x) == used.end() && x < ULL(goal) + distance){
	    if(x == ULL(goal)) return result;
	    next.insert(x);
	    if(goal - x > 0 && goal - x < distance){
	      distance = goal - x;
	    }
	    if(x - goal > 0 && x - goal < distance){
	      distance = x - goal;
	    }
	  }
	}
	// increment
	x = n + 1;
	if(used.find(x) == used.end() && x <= goal){
	  if(x == ULL(goal)) return result;
	  next.insert(x);
	  if(goal - x < distance){
	    distance = goal - x;
	  }
	}
      }
      if(skip) return result + distance;
      cur = next;
      next.clear();
    }
  }