見出し画像

[ARC130] C - Digit Sum Minimization

[Q] https://atcoder.jp/contests/arc130/tasks/arc130_c

かなり貪欲だけど1発で通る。ラッキーパンチあてたと思っている。

考察してみる。繰り上げ発生ごとに、桁の総和が9減少することに気づく。

Q. 入力例
A = 253
B = 266

・もし繰り上げがなければ
532
266
---
798 = 7+9+8 = 24
これは、全部の総和 2+5+3+2+6+6 = 24 と一致する。

・繰り上げが1回発生した場合は
 532
 662
----
1194 = 1+1+9+4 = 15
24-15 = 9

繰り上げが 1 回発生するごとに、桁の総和は、9 ずつ減少するのがわかる。

 ​なので、繰り上げ回数を最大化すれば解答が得られると思う。

1. 桁が小さいほうをAにする。
2. とりあえず「10以上の組み合わせを1個見つける」を全探索する。
-3. 10以上の組み合わせが1個も作れない場合は終了。
-3. 作れた場合「9以上の組み合わせ数」が最も大きくなる[2.]を探す。
4. Aが全部繰り上がりになる場合、Bで残す9の数も繰り上げ数に含める。
5. [2.]の、最適な10以上の1つの組み合わせを決めたら、あとは9以上の組み合わせを機械的に採用する。
6. 残ったBを、大きい順に配置しておしまい。 

Q. 入力例3
123
987987

1. A.size() < B.size() なので今回はABはそのまま。
2. 10以上の組み合わせを探して、見つかったら都度9以上の組み合わせ数を全探索していく。

・桁和10の場合
1) (A,B) = (1,9)がとれる。
1の位を固定する
A =    XX1
B = XXXXX9
この状態から、9以上の組み合わせ数を探索する。
A =    321
B = 889779
がベスト。この繰り上がり数は、
A =    321
B = 889779
      UUUU 4か所であがるので、4 繰り上がり。候補(1,9)をメモしておく

2) (A,B) = (2,8)がとれる。
1の位を固定する
A =    XX2
B = XXXXX8
この状態から、9以上の組み合わせ数を探索する。
A =    312
B = 799788
がベスト。この繰り上がり数は、
A =    312
B = 799788
     UUUUU 5か所であがるので、5 繰り上がり。候補(2,8)に更新する。

2) (A,B) = (3,7)がとれる。
1の位を固定する
A =    XX3
B = XXXXX7
この状態から、9以上の組み合わせ数を探索する。
A =    213
B = 899787
がベスト。この繰り上がり数は、
A =    213
B = 899787
     UUUUU 5か所であがるので、5 繰り上がり。更新しないのでスルー。

・桁和11の場合
(A,B) = {(2,9), (3,8)} の2通りも同様に探索するが、更新なし。

・桁和12の場合
(A,B) = {(3,9)} の1通りも同様に探索するが、更新なし。

5. 最適な1の位が(2,8) に定まる。
なので、あとは機械的に9以上の組み合わせを採用していく。
A =    XX2
B = XXXXX8

A =    312
B = XXX788

6. B は 799 が残っている。大きい値から配置していく。
A =    312
B = 799788

これが解答。

実装

string A, B;
ll asz, bsz;
ll numA[10]; // A="111" なら、numA[1]=3
ll numB[10];
bool sp = false; // Aの桁数が大きい場合ABをswap。その復元flag

// 桁和9以上の組み合わせが何通りできるか探索する
ll check2(ll defa, ll defb){// (defa,defb): 固定した1の位
 ll testA[10];
 ll testB[10];
 rep(i, 10) testA[i] = numA[i];
 rep(i, 10) testB[i] = numB[i];
 --testA[defa];
 --testB[defb];
 
 ll ups = 0; // 桁和9以上の数
 for(ll i=9; i<=18; ++i){ // 作りたい組み合わせの総和
   for(ll a=i-9; a<=9; ++a){ // 8+9+8+7+6+...+1
     if(a==0) continue;
     ll b=i-a;
     ll add = min(testA[a], testB[b]);
     ups += add;
     testA[a] -= add;
     testB[b] -= add;
   }
 }
 if (ups == asz-1){ // Aが全部繰り上げの場合、Bの"9"も繰り上げに含める
   ups += testB[9];
 }
 return ups;
}

// 固定する1の位を探索
pll check(){
 ll kumi=0;
 pll ret = {0, 0}; // 固定する1の位の解答
 for(ll i=10; i<=18; ++i){ // 作りたい組み合わせ 9+8+7+...+1
   for(ll a=i-9; a<=9; ++a){
     ll b=i-a;
     ll add = min(numA[a], numB[b]);
     if (add>0){ // もし桁和10以上の組み合わせが見つかったら
       ll score = check2(a, b);
       if (chmax(kumi, score)){
         ret.first = a;
         ret.second = b;
       }
     }
   }
 }
 return ret;
}

int main(){
 cincout();
 
 cin >> A >> B;
 stack<char> ansA; // 1の位から答えを入れていくのでstack
 stack<char> ansB;
 asz = A.size();
 bsz = B.size();
 if(asz>bsz){
   swap(A, B);
   asz = A.size();
   bsz = B.size();
   sp = true;
 }
 rep(i, asz) ++numA[A[i]-'0'];
 rep(i, bsz) ++numB[B[i]-'0'];

 // 固定する1の位を1つみつける。桁和10以上を1つだけ。
 pll P = check(); // {a, b}
 if (P.first==0){ // 繰り上げが1個もなければ終了
   if(sp) swap(A, B); // わすれてた
   cout << A << endl;
   cout << B << endl;
   return 0;
 }
 // 1の位を固定。
 ansA.push(P.first+'0');
 ansB.push(P.second+'0');
 --numA[P.first];
 --numB[P.second];
 // 9以上の組み合わせを、桁和9から決定していく
 for(ll i=9; i<=18; ++i){ // 作りたい組み合わせの桁和
   for(ll a=i-9; a<=9; ++a){ // 8+9+8+7+...+1
     if (a==0) continue;
     ll b=i-a;
     ll add = min(numA[a], numB[b]);
     while(add>0){
       ansA.push(a+'0');
       ansB.push(b+'0');
       --numA[a];
       --numB[b];
       --add;
     }
   }
 }
 // 9以上にできなかった桁和を、大きい値から入れていく。
 for(ll i=9; i>=1; --i){
   while (numA[i]>0){
     ansA.push(i+'0');
     --numA[i];
   }
   while (numB[i]>0){
     ansB.push(i+'0');
     --numB[i];
   }
 }

 string AA, BB;
 while (!ansA.empty()){
   AA += ansA.top();
   ansA.pop();
 }
 while (!ansB.empty()){
   BB += ansB.top();
   ansB.pop();
 }

 if (sp) swap(AA, BB); // もし入れ替えてたなら復元
 cout << AA << endl;
 cout << BB << endl;
}

https://atcoder.jp/contests/arc130/submissions/27575870

Q.
1234
123
のとき?

A. 
123
1234
になっちゃってた。繰り上がりがないときの復元swap忘れてた。救われた。

いいなと思ったら応援しよう!