TopCoder > SRM596_IncrementAndDoubling

TopCoder SRM 596 Div 1 Easy : IncrementAndDoubling

問題概要

N個の要素からなる、0で初期化された配列がある。
この配列に対して、以下の2つの操作を行うことができる。

  • 配列の任意の1つの要素に対して1を足す。
  • 配列の全ての要素を2倍する。

引数として、N個の[0,1000]の整数からなる配列が与えられる。
最小何回の操作で、引数の配列と等しくすることができるか答えよ。

解法1

貪欲に解く。
0の配列の状態からスタートするのではなく、逆からスタートして計算する。
つまり、引数で与えられた配列の形からスタートして、0の配列にできる、最小のステップ数を求めると考える。

こう考えれば、ある配列の状態から、以下の2つの操作で一意に遷移しつつ、0の配列を目指すことができる。

  • 奇数の値が配列の中に一つでも入っている場合
    • 奇数になっている値全てから1を引く
    • 1を引いた回数の分をコストに足す
  • 全ての値が偶数の場合
    • 2で割れるので、全ての値を2で割る
    • コストに1足す

解法2

まず、[0,1000]の各数について、次のように動的計画法(DP)を使って最小コストを求めておく。

dp[number (1000)][doub (10)] =
  numberにするために、2倍操作をdoub回行ったときの、
  インクリメント操作の最小回

2倍の操作は、高々9回だけ行えばいい。10回以上行うと1000を超えるから。

次に、配列について考える。
配列の各値において、2倍操作の回数は全て共通で変わらないはずである。
そのため、2倍操作の回数だけループで決めてやって、先程のdp配列を利用してコストを足し合わせればよい。

プログラム1(解法1ver.)

  1. class IncrementAndDoubling {
  2. public:
  3.  
  4. bool finished(const vector<int> v){
  5. for(int i = 0; i < v.size(); i++){
  6. if(v[i] != 0) return false;
  7. }
  8. return true;
  9. }
  10.  
  11. int getMin(vector <int> desiredArray) {
  12. int res = 0;
  13.  
  14. while(!finished(desiredArray)){
  15. bool doubleFlg = true;
  16. for(int i = 0; i < desiredArray.size(); i++){
  17. if(desiredArray[i] % 2 != 0){
  18. desiredArray[i]--;
  19. res++;
  20. doubleFlg = false;
  21. }
  22. }
  23.  
  24. if(!doubleFlg) continue;
  25.  
  26. res++;
  27. for(int i = 0; i < desiredArray.size(); i++){
  28. desiredArray[i] /= 2;
  29. }
  30. }
  31.  
  32. return res;
  33. }
  34. };

プログラム2(解法2ver.)

  1. int dp[1002][12];
  2.  
  3. class IncrementAndDoubling {
  4. public:
  5. int getMin(vector <int> desiredArray) {
  6. int n = desiredArray.size();
  7.  
  8. for(int j = 0; j < 1002; j++){
  9. for(int k = 0; k < 12; k++){
  10. dp[j][k] = INT_MAX;
  11. }
  12. }
  13.  
  14. dp[0][0] = 0;
  15.  
  16. for(int j = 0; j < 1000; j++){
  17. for(int k = 0; k <= 10; k++){
  18. if(dp[j][k] == INT_MAX) continue;
  19.  
  20. dp[j + 1][k] = min(dp[j + 1][k], dp[j][k] + 1);
  21. if(j * 2 > 1000) continue;
  22. dp[j * 2][k + 1] = min(dp[j * 2][k + 1], dp[j][k]);
  23. }
  24. }
  25.  
  26. int res = INT_MAX;
  27.  
  28. for(int i = 0; i <= 10; i++){
  29. int sum = i;
  30.  
  31. for(int j = 0; j < n; j++){
  32. if(dp[desiredArray[j]][i] == INT_MAX){
  33. sum = INT_MAX;
  34. break;
  35. }
  36. sum += dp[desiredArray[j]][i];
  37. }
  38.  
  39. res = min(res, sum);
  40. }
  41.  
  42. return res;
  43. }
  44. };
最終更新:2013年11月02日 13:45