競プロやります。

競プロを頑張るブログです。

AtCoder Beginner Contest 118

C - Monsters Battle Royale

https://atcoder.jp/contests/abc118/tasks/abc118_c

方針

 A_1,~\dots,~A_N の最大公約数  g が答えになりますが、コンテスト中には気付けませんでした。結局全く同じことを遠回しにしただけですが、以下のように考えました:

  1.  A_i > 0 のうち最小のものを  A_m = a とする。
  2.  A_i~(i \neq m) について、 a で割った余りに置き換える。
  3. 以上 1, 2 を  A_i > 0 なる  i が 1 つだけになるまで繰り返す。

最後に残った  A_i > 0 が答えになります。

ソースコード

#include <bits/stdc++.h>
using namespace std;

int main(){
    int N;  cin >> N;
    vector<int> A(N);
    for(int i=0; i<N; i++)  cin >> A[i];

    int m = 0;
    while(true){
        sort(A.begin(), A.end()); 
        m = lower_bound(A.begin(), A.end(), 1) - A.begin(); // 上記の 1. 
        if(m==N-1) break; // m == N-1 のとき、正の A[i] が 1 つだけ。

        for(int i=m+1; i<N; i++) A[i] = A[i]%A[m]; // 上記の 2. 
    }
    cout << A[m] << endl;
}

D - Match Matching

https://atcoder.jp/contests/abc118/tasks/abc118_d

方針

問題文で与えられている、整数  A_i を作るのに必要なマッチ棒の数を  n_i とします。

 dp_i = マッチ棒をちょうど  i 本使って作れる整数の最大値、とします。これは64ビット整数に収まらないことがあるので string 型にしておきます。マッチ棒  i 本で作れる整数がないときは空文字列 "" にします。求めたいものは  dp_N です。

まず 各  n_i について  dp_{n_i} を求めることが出来ます。 次に  dp_i が分かっているときに  dp_{i+n_j} を求めることを考えます。
(1)  dp_{i+n_j} の桁数 <  dp_i の桁数 + 1 のとき
 dp_i の適切な位置に  A_j を挿入したものを  dp_{i+n_j} とします。挿入の際は  dp _i を上位桁から見ていき、  k 文字目が  A_j 以下になったときに、 k 文字目の前に  A_j を挿入します。

たとえば 9954332 に 4 を挿入すると、99544332 となり、974421 に 5 を挿入すると、 9754421 となります。

なお、 dp_{n_i} 本のマッチ棒で作る整数は一桁なので、文字列としてみた時に降順に並んでいます。降順な文字列から出発してこの挿入を繰り返すので、 dp_i は必ず降順な文字列になっています。

(2)  dp_{i+n_j} の桁数 =  dp_i の桁数 + 1 のとき
(1)の方法で得られる整数と  dp_{i+n_j} を比較し、大きい方を  dp_{i+n_j} とします。

(3)  dp_{i+n_j} の桁数 >  dp_i の桁数 + 1 のとき
このときは  dp_{i+n_j} を更新する必要はありません。

ソースコード

#include <bits/stdc++.h>
using namespace std;

// 一桁の数 i を作るのに必要なマッチ棒の数を nMatch[i] として持っておく
const int nMatch[10] = {0,2,5,5,4,5,6,3,7,6};

string append(string s, string t){
    // s は必ず降順の文字列なので、
    // s[i] <= t となる s[i] の直前に t を挿入する
    int n = s.length();
    for(int i=0; i<n; i++){
        if(s[i] <= t[0]){
            s.insert(i,t);
            break;
        }
        // s の最後の文字の手前にも t を挿入できないときは、
        // s の一番後ろに付け足す。
        if(i==n-1) s += t;
    }
    return s;
}

int main(){
    int N, M;
    vector<int> A;
    cin >> N >> M;
    A = vector<int>(M,0);
    for(int i=0; i<M; i++) cin >> A[i];

    vector<string> dp(N+1,"");
    // 1 桁の数字 A[j] を作る場合に関して dp[i] を求めておく
    for(int i=0; i<M; i++){
        int n = nMatch[A[i]];
        if(n > N) continue;
        if(dp[n] == "" || dp[n] < to_string(A[i])) 
            dp[n] = to_string(A[i]);
    }

    for(int i=1; i<=N; i++){
        if(dp[i]=="") continue;
        for(int j=0; j<M; j++){
            // dp[i] に A[j] を追加すると、マッチ棒は合わせて nxt 本になる
            int nxt = i + nMatch[A[j]];
            if(nxt > N) continue;
            if(dp[nxt].length() < dp[i].length() + 1){  // (1)の場合
                dp[nxt] = append(dp[i], to_string(A[j]));
            }else if(dp[nxt].length() == dp[i].length() + 1){  // (2)の場合
                string tmp = append(dp[i], to_string(A[j]));
                if(dp[nxt] < tmp) dp[nxt] = tmp;
            }
        }
    }
    cout << dp[N] << endl;
}