20
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【1〜15問目】AtCoderの過去問精選100問をC++, Pythonで解く

Last updated at Posted at 2020-04-08

はじめに

赤コーダーの @e869120 さんが先日素晴らしい記事を書かれました。

レッドコーダーが教える、競プロ・AtCoder上達のガイドライン【初級編:競プロを始めよう】
レッドコーダーが教える、競プロ・AtCoder上達のガイドライン【中級編:目指せ水色コーダー!】
レッドコーダーが教える、競プロ・AtCoder上達のガイドライン【上級編:目指せレッドコーダー!】

この中でも特に「中級編」は緑コーダーの僕にとってまさに Looks Good To Me でしたので、
下記のような宣言のもと、記事内の「精選100問」を解く日々が始まりました・・。

気付けば10日突破!10問解いたということで、記事を書こうと思った次第です。
解いた問題の備忘録、および思考の整理がメインではありますが、読まれた方に何か気付きがあればとても嬉しいです。

注意

「競技プログラミング的」な記法が多くなると思います。ご了承頂けると嬉しいです。
具体的にはC++ですと

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

を最初にテンプレートとして記述しています。

1日目 AOJ ITP1_7_B How many ways?

$1$ から $N$ までの数の中から、重複無しで3つの数を選びそれらの合計が $X$ となる組み合わせの数を求める

  • $3 ≤ n ≤ 100$
  • $0 ≤ x ≤ 300$

C++

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

int main() {

    while(1){ // 入力数が決まってないため無限ループを作る
        int n, x;
        cin >> n >> x; // 入力受け取り

        if(n==0) return 0; // 3 <= n なので n==0 きたら終わり

        int res = 0; // 満たすものがない時は0
        for(int a = 1; a <= n-2; ++a){ // 全ての組み合わせを調べるループ、重複と入れ替えを除くやつ
            for(int b = a+1; b <= n-1; ++b){
                for(int c = b+1; c <= n; ++c){ // 制約がもっと厳しい場合はこのループをなくしてc=x-a-bが「a,bと異なること」「1以上n以下であること」を調べる

                    if(a+b+c == x) ++res; // 条件を満たす時結果をインクリメント
                }
            }
        }

        cout << res << endl; // 結果を出力。クエリ問題は << "\n" の方がいいですか?
    }

    return 0;
}

Python

while(True): # 入力数が決まってないため無限ループを作る
    n, x = map(int, input().split()) # 入力受け取り

    if(n==0): break # 3 <= n なので n==0 きたら終わり
    
    res = 0 # 満たすものがない時は0
    for a in range(1, n-1): # 全ての組み合わせを調べるループ、重複と入れ替えを除くやつ
        for b in range(a+1, n):
            for c in range(b+1, n+1): # 制約がもっと厳しい場合はこのループをなくしてc=x-a-bが「a,bと異なること」「1以上n以下であること」を調べる
                
                if(a+b+c == x): res += 1 # 条件を満たす時結果をインクリメント。 ++res としないように
    
    print(res)

2日目 AtCoder Beginner Contest 106 B - 105

$1$ 以上 $N$ 以下の奇数のうち, 正の約数を ちょうど $8$ 個持つようなものの個数を求める

  • $N$ は $1$ 以上 $200$ 以下の整数

C++

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int divisor(int num){ // 約数の個数を返してくれる関数
    int ret = 0;
 
    for(int i = 1; i <= num; ++i){
        if(num%i == 0) ++ret;
    }
 
    return ret;
}
 
int main() {
 
    int n;
    cin >> n;
 
    if(n <= 105){ // 出力例1 に「ただ一つ」というヒントがあるので大人しく従います。
        if(n == 105) cout << 1 << endl;
        else cout << 0 << endl;
        return 0;
    }
 
    int res = 1; // 105です
    for(int num = 106; num <= n; ++num){
        if(divisor(num)==8 && num%2==1) ++res; // 「約数の個数がちょうど8の奇数」をそのまま実装
    }
 
    cout << res << endl;
 
    return 0;
}

Python

def divisor(num): # 約数の個数を返してくれる関数
    
    ret = 0
    for i in range(1, num+1):
        if(num%i == 0): ret += 1
    
    return ret

n = int(input())

if(n <= 105): # 出力例1 に「ただ一つ」というヒントがあるので大人しく従います。
    if(n==105): print(1)
    else: print(0)
    exit()

res = 1
for i in range(106, n+1):
    if(divisor(i) == 8 and i%2==1): res += 1 # 「約数の個数がちょうど8の奇数」をそのまま実装

print(res)

3日目 AtCoder Beginner Contest 122 B - ATCoder

文字列 $S$ の部分文字列のうち最も長い 「'A', 'C', 'G', 'T' 以外の文字を含まない文字列」の長さを求める

  • $S$ は長さ $1$ 以上 $10$ 以下の文字列である。
  • $S$ の各文字は英大文字である。

C++

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int main() {
 
    string s;
    cin >> s;
    int n = s.size(); // 文字列の長さを取得しておく
 
    int res = 0; // 条件になければ0
    for(int i = 0; i < n; ++i){ // Sの部分文字列を全探索するループ:重複を許す(i==jの時は1文字の部分文字列とする)が入替を許さないループ
        for(int j = i; j < n; ++j){
            string sub = s.substr(i, j-i+1); // i文字目からからj文字目までの部分文字列
            int num = sub.size();
 
            bool flg = true; // ACGT文字列フラグ
            for(int k = 0; k < num; ++k){
                char c = sub[k]; // 部分文字列の文字cのうち
                if(c!='A' && c!='C' && c!='G' && c!='T') { // 一つでもACGT以外があったら
                    flg = false; // flgをfalseにして
                    break; // つぎにいく
                }
            }
 
            if(flg) res = max(res, num); // ACGT文字列の長さnumの最大値を保持する
        }
    }
 
    cout << res << endl;
 
 
    return 0;
}

Python

s = input()
n = len(s) # 文字列の長さを取得しておく

res = 0 # 条件になければ0
for i in range(n): # Sの部分文字列を全探索するループ:重複を許す(i==jの時は1文字の部分文字列になる)が入替を許さないループ
    for j in range(i, n):
        sub = s[i: j+1] # i文字目からからj文字目までの部分文字列
        
        flg = True
        for c in sub: # Python的for文
            if(c not in ['A', 'G', 'C', 'T']): flg = False # こちらも、in はこういう時便利です
        
        if(flg): res = max(res, len(sub)) # ACGT文字列の長さnumの最大値を保持する

print(res)

割とイケてる実装。はたして?

Python その2

s = input()
s_str = '' # からの文字列を作る
 
for c in s: # sを1文字ずつ走査
    if(c in ('A', 'C', 'G', 'T')): s_str += c # cがACGTどれかならs_strに追加
    else: s_str += ' ' # それ以外の時はスペースを追加 → ACJKAJAGTみたいなのは'AC  A AGT'みたいになる
 
if(s_str.split() == []): print(0) # ACGTが含まれない文字列はこちらになる
else: print(len(max(s_str.split(), key = len))) # ['AC', 'A', 'AGT']みたいになるのでこの配列の要素のうち「文字列の長さの最大値」を出力する

4日目 パ研合宿2019 第3日「パ研杯2019」 C - カラオケ

$N$ 人が $M$ 曲のどれかを歌う。番号 $i$ の生徒が曲 $j$ を歌うと $Ai,j$ 点を取る。

  • $M$ 曲の中から $2$ つの曲を選ぶ(それぞれ $T1$ と $T2$ とする)
  • それぞれの生徒が曲 $T1$ と曲 $T2$ の両方を歌う
  • 各生徒の得点はその生徒が歌った $2$ つの曲の点数のうち高い方となる
  • グループの得点は,生徒 $1,2,...,N$ の得点の合計となる

そのときグループの得点として考えられる最大の値を求める

C++

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int main() {
 
    ll n, m;
    cin >> n >> m;
 
    vector<vector<ll>> a(n, vector<ll>(m)); // (n, m) の2次元配列を宣言
    for(int i = 0; i < n; ++i){
        for(int j = 0; j < m; ++j){
            cin >> a[i][j];
        }
    }
 
    ll res = 0; // 制約より負の得点はないので0で初期化
    for(int one = 0; one < m; ++one){ // 一曲目、決め打ち
        for(int two = 0; two < m; ++two) { // 二曲目、決め打ち
            ll sum = 0; // 得点の合計、0で初期化

            for(int i = 0; i < n; ++i){
                sum += max(a[i][one], a[i][two]); // 「2つの曲の点数のうち高い方」をそのまま実装
            }
            res = max(res, sum); // この一曲目・二曲目の選び方だとsum点。resに最大値を保持
        }
    }
 
    cout << res << endl;
 
    return 0;
}

Python

n, m = map(int, input().split())
a = [list(map(int, input().split())) for _ in range(n)] # 行数しか指定要らない例のやつ

res = 0 # 制約より負の得点はないので0で初期化
for one in range(m): # 一曲目、決め打ち
    for two in range(m): # 二曲目、決め打ち
        sum = 0 # 得点の合計、0で初期化
        
        for i in range(n):
            sum += max(a[i][one], a[i][two]) # 「2つの曲の点数のうち高い方」をそのまま実装
        
        res = max(res, sum) # この一曲目・二曲目の選び方だとsum点。resに最大値を保持

print(res)

5日目 AtCoder Beginner Contest 095 C - Half and Half

$A$ ピザ、$B$ ピザ、$AB$ ピザ $1$ 枚あたりの値段はそれぞれ $A$ 円、$B$ 円、$C$ 円
$A$ ピザ $X$ 枚と $B$ ピザ $Y$ 枚を用意するために最小で何円が必要か?

 - $1≤A,B,C≤5000$
 - $1≤X,Y≤105$
 - 入力中のすべての値は整数である。

C++

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int main() {
    
    int a, b, c, x, y;
    cin >> a >> b >> c >> x >> y;
 
    int res = a*x + b*y; // 条件を満たす買い方のうちABピザの枚数(*2)を全探索する。ABピザを一切買わない場合を一旦答えにする(これを大きい値にして、for ループを0からにしてもよい)
    for(int i = 1; i <= max(x, y); ++i){ // 全部ABピザでまかなう時は x,yの内多い方分買うことになる
        int ab = i*2; // ABピザを買う枚数奇数枚買うことはないので2倍
        int cand = c*ab + a*max(0, x-i) + b*max(0, y-i); // i枚分はABピザになるので-i 負の値にはならない(あまりになる)ので0とのmaxをとる
 
        res = min(res, cand); // 候補との最小値を保持
    }
 
    cout << res << endl;
 
}

Python

a, b, c, x, y = map(int, input().split())

res = a*x + b*y # 条件を満たす買い方のうちABピザの枚数(*2)を全探索する。ABピザを一切買わない場合を一旦答えにする(これを大きい値にして、for ループを0からにしてもよい)
for i in range(1, max(x, y)+1): # 全部ABピザでまかなう時は x,yの内多い方分買うことになる
    ab = i*2 # ABピザを買う枚数奇数枚買うことはないので2倍
    cand = ab*c + a*max(0, x-i) + b*max(0, y-i) # i枚分はABピザになるので-i 負の値にはならない(あまりになる)ので0とのmaxをとる
    
    res = min(res, cand) # 候補との最小値を保持

print(res)

6日目 三井住友信託銀行プログラミングコンテスト2019 D - Lucky PIN

C++

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int main() {
 
    int n;
    cin >> n;

    string s;
    cin >> s;
 
    int res = 0;
    for(int i = 0; i < 1000; ++i){ // PINの組み合わせは高々1000通りなのでこちらを全探索
        char one = '0' + (i/100); // (左から数えて)一つ目
        char two = '0' + (i%100/10); // 二つ目
        char three = '0' + (i%10); // 三つ目。これらがこの順番にsに登場するかを調べる
 
        bool flg1 = false; // 一つ目がある
        bool flg2 = false; // 二つ目が一つ目より右側にある
        bool flg3 = false; // 三つ目が一つ目、二つ目より右側にある
 
        int ptr = 0; // 現在の走査位置を表すポインタ
        for(int j = ptr; j < n-2; ++j){ // 一つ目を探すループ(n-2までみてなかったらあってもNG)
            if(s[j] == one){
                flg1 = true; // 一つ目があった
                ptr = j+1; // 二つ目は一つ目のひとつ右から走査開始
                break; // これをしないと一番右側の一つ目を採用しちゃう
            }
        }
 
        for(int j = ptr; j < n-1; ++j){ // 一つ目より右から二つ目を探す
            if(s[j] == two){
                flg2 = true;
                ptr = j+1;
                break;
            }
        }
 
        for(int j = ptr; j < n; ++j){ // 二つ目より右から三つ目を探す
            if(s[j] == three) {
                flg3 = true;
                break;
            }
        }
 
        if(flg1 && flg2 && flg3) ++res; // 条件を満たせばflgが全部trueとなりその時だけ結果をインクリメント
    }
 
    cout << res << endl;
 
    return 0;
}

Python

n = int(input())
s = input()

res = 0
for i in range(1000): # PINの組み合わせは高々1000通りなのでこちらを全探索
    one = str(i//100) # (左から数えて)一つ目
    two = str(i%100//10) # 二つ目
    three = str(i%10) # 三つ目。これらがこの順番にsに登場するかを調べる
    
    flg1 = False
    flg2 = False
    flg3 = False
    
    ptr = 0 # 現在の走査位置を表すポインタ
    for j in range(ptr, n-2): # 一つ目を探すループ(n-2までみてなかったらあってもNG)
        if(s[j] == one):
            flg1 = True # 一つ目があった
            ptr = j+1 # 二つ目は一つ目のひとつ右から走査開始
            break # これをしないと一番右側の一つ目を採用しちゃう
    
    for j in range(ptr, n-1): # 一つ目より右から二つ目を探す
        if(s[j] == two):
            flg2 = True
            ptr = j+1
            break
    
    for j in range(ptr, n): # 二つ目より右から三つ目を探す
        if(s[j] == three):
            flg3 = True
            break
    
    if(flg1 and flg2 and flg3): res += 1 # 条件を満たせばflgが全部trueとなりその時だけ結果をインクリメント

print(res)

※ちなみにこの解法はPython3だとTLEになってしまうのでPyPy3で提出しましょう😭

7日目 第6回日本情報オリンピック 本選(オンライン) C - 最古の遺跡

C++

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int main() {
 
    int n;
    cin >> n;
 
    vector<int> x(n), y(n); // 座標
    set<pair<int, int>> st; // 座標のセット、後で「この座標があるかどうか?」参照するため高速で動作する必要がある
    for(int i = 0; i < n; ++i){
        cin >> x[i] >> y[i];
        st.insert(make_pair(x[i], y[i]));
    }
 
    int res = 0;
    for(int i = 0; i < n-1; ++i){ // i,jの二重ループで「二本の柱=正方形の1辺候補」を全探索
        for(int j = i+1; j < n; ++j){
            pair<int, int> vec1 = make_pair(x[j] - x[i], y[j] - y[i]); // 2本の柱からベクトルを作っておく
            int s = (x[j]-x[i])*(x[j]-x[i]) + (y[j]-y[i])*(y[j]-y[i]); // 1辺がわかれば面積がわかるので先に計算しておく
 
            if(s < res) continue; // 暫定最大面積に及ばなければこの後は見る必要がない
 
            vector<pair<int, int>> vec2_cand(2); // 1辺が決まるとベクトルでいう始点から伸びるもう1辺は2通りしかない
            vec2_cand[0] = make_pair(vec1.second, -vec1.first); // 2つ目のベクトル、候補1(ベクトル1に対して内積が0になるベクトルが二つある)
            vec2_cand[1] = make_pair(-vec1.second, vec1.first); // もう一つ

            for(auto vec2: vec2_cand){ // 二つの候補について調べる
                if(st.find(make_pair(vec2.first + x[i], vec2.second + y[i])) != st.end()){ // ベクトル2が存在するか。※ベクトル1の始点を加えて座標に変換することを忘れない
                    
                    pair<int, int> vec3 = make_pair(vec1.first + vec2.first, vec1.second + vec2.second); // もし存在するなら、ベクトル3はベクトル1 + ベクトル2で確定なのでそれが存在するかを調べる
                    if(st.find(make_pair(vec3.first + x[i], vec3.second + y[i])) != st.end()){
                        res = max(res, s); // ベクトル3の座標もあればそこに正方形を作れるので暫定TOPと戦う
                    }
                }
            }
        }
    }
    cout << res << endl;
    return 0;
}

Python

n = int(input())
xy = [tuple(map(int, input().split())) for _ in range(n)] # 「この座標が存在するか?」を高速に「setへのin」で確かめたいのでhash可能なtuple型で受け取る
 
st = set() # 座標をセットで保持
for i in range(n):
    st.add(xy[i])
 
res = 0
for i in range(n-1): # i,jの二重ループで「二本の柱=正方形の1辺候補」を全探索
    for j in range(i+1, n):
        vec1 = xy[j][0] - xy[i][0], xy[j][1] - xy[i][1] # 2本の柱からベクトルを作っておく
        s = vec1[0]*vec1[0] + vec1[1]*vec1[1] # 1辺がわかれば面積がわかるので先に計算しておく
        
        if(s < res): continue # 暫定最大面積に及ばなければこの後は見る必要がない
        
        vec2_cand = (vec1[1], -vec1[0]), (-vec1[1], vec1[0]) # 1辺が決まるとベクトルでいう始点から伸びるもう1辺は2通りしかない(ベクトル1に対して内積が0になるベクトルが二つある)
        
        for vec2 in vec2_cand: # 二つの候補について調べる
            
            if(((vec2[0] + xy[i][0]), (vec2[1] + xy[i][1])) in st): # ベクトル2が存在するか。※ベクトル1の始点を加えて座標に変換することを忘れない
                
                vec3 = vec1[0] + vec2[0], vec1[1] + vec2[1] # ベクトル3はベクトル1 + ベクトル2で確定なのでそれが存在するかを調べる
                
                if(((vec3[0] + xy[i][0]), (vec3[1] + xy[i][1])) in st): # vec3 exists
                    res = max(res, s) # ベクトル3の座標もあればそこに正方形を作れるので暫定TOPと戦う
 
print(res)

tupleset に入れられるのでそちらに直しています。

8日目 square869120Contest #6 B - AtCoder Market

C++

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int main() {
 
    ll n;
    cin >> n;

    vector<ll> a(n), b(n);
    rep(i, n) cin >> a[i] >> b[i];
 
    ll res = 1LL << 60;
    for(int i = 0; i < n; ++i){ // スタートとゴールの位置を「商品のあるマス」に決めうちし全探索このとき「s==g」は許容するが「g < s」は除外する
        for(int j = 0; j < n; ++j){
            ll s = a[i];
            ll g = b[j];
            if(g < s) continue;
 
            ll cost = 0;
            for(int k = 0; k < n; ++k){ // n人の買い物コストを計算していく
 
                ll l = a[k]; // 左の商品と
                ll r = b[k]; // 右の商品のスタート、ゴールとの位置関係で場合わけする
 
                if(s<=l && r<=g) cost += g-s; // 商品が両方s〜gにある時
                else if(l<=s && r<=g) cost += (s-l)*2 + g-s; // lの商品がsより左(lを買って一旦sに戻る)
                else if(s<=l && g<=r) cost += r-s + r-g; // rの商品がgより右(rを買ってからgにいく)
                else if(l<=s && g<=r) cost += (s-l)*2 + (r-g)*2 + g-s; // 商品が両方外側(遠回りする)
 
            }

            res = min(res, cost); // 暫定最小コストと戦う
        }
    }
 
    cout << res << endl;
 
    return 0;
}

Python

n = int(input())
ab = [list(map(int, input().split())) for _ in range(n)]

res = 1 << 60
for i in range(n): # スタートとゴールの位置を「商品のあるマス」に決めうちし全探索このとき「s==g」は許容するが「g < s」は除外する
    for j in range(n):
        s = ab[i][0]
        g = ab[j][1]
        
        if(g < s): continue
        
        cost = 0
        for k in range(n): # n人の買い物コストを計算していく
            l = ab[k][0] # 左の商品と
            r = ab[k][1] # 右の商品のスタート、ゴールとの位置関係で場合わけする
            
            if(s<=l and r<=g): cost += g-s # 商品が両方s〜gにある時
            elif(l<=s and r<=g): cost += (s-l)*2 + g-s # lの商品がsより左(lを買って一旦sに戻る)
            elif(s<=l and g<=r): cost += r-s + r-g # rの商品がgより右(rを買ってからgにいく)
            elif(l<=s and g<=r): cost += (s-l)*2 + (r-g)*2 + g-s # 商品が両方外側(遠回りする)
            
        res = min(res, cost) # 暫定最小コストと戦う

print(res)

9日目 第7回日本情報オリンピック 予選(オンライン) D - 星座探し

C++

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int main() {
 
    int m;
    cin >> m;
    
    vector<int> xm(m), ym(m);
    vector<pair<int, int>> vec(m);
    for(int i = 0; i < m; ++i) {
        cin >> xm[i] >> ym[i];
        vec[i] = make_pair(xm[i] - xm[0], ym[i] - ym[0]); // x0, y0を基準に全ての座標の相対位置(ベクトル)を保存しておく
    }
 
    int n;
    cin >> n;

    vector<int> xn(n), yn(n);
    set<pair<int, int>> xyn;
    for(int i = 0; i < n; ++i) {
        cin >> xn[i] >> yn[i];
        xyn.insert(make_pair(xn[i], yn[i])); // 星の座標のペアを全てsetに入れ高速に参照できるようにしておく
    }
 
    for(int i = 0; i < n; ++i){
        int x = xn[i]; // n個の星のうちi番目が星座の構成要素0番目であると仮定、全探索する
        int y = yn[i];
 
        bool flg = true; // 星座の構成要素が全てあれば守られるフラグ
        for(int j = 0; j < m; ++j){
            pair<int, int> nxt = make_pair(x+vec[j].first, y+vec[j].second); // i番目の星が星座の構成0番目だとするとj番目の構成要素はi番目の星+j番目の相対位置ベクトル
            if(xyn.find(nxt) == xyn.end()) { // そんな星がない時は
                flg = false; // フラグを折って
                break; // 処理を中止する
            }
        }
 
        if(flg){
            cout << xn[i] - xm[0] << " " << yn[i] - ym[0] << endl; // 見つかったら平行移動分を表示してプログラムを終える
            return 0;
        }
    }
 
 
    return 0;
}

Python

m = int(input())
xym = [tuple(map(int, input().split())) for _ in range(m)] # listでもいい

vec = [(xym[i][0] - xym[0][0], xym[i][1] - xym[0][1]) for i in range(m)] # x0, y0を基準に全ての座標の相対位置(ベクトル)を保存しておく

n = int(input())
xyn = [tuple(map(int, input().split())) for _ in range(n)] # setに入れたいのでタプル
xyn_st = set()
for xy in xyn:
    xyn_st.add(xy) # 星の座標のペアを全てsetに入れ高速に参照できるようにしておく

for i in range(n):
    x = xyn[i][0] # n個の星のうちi番目が星座の構成要素0番目であると仮定、全探索する
    y = xyn[i][1]
    
    flg = True # 星座の構成要素が全てあれば守られるフラグ
    for j in range(m):
        nxt = x+vec[j][0], y+vec[j][1] # i番目の星が星座の構成0番目だとするとj番目の構成要素はi番目の星+j番目の相対位置ベクトル(この書き方だとtuple型で代入される)
        
        if(nxt not in xyn_st): # そんな星がない時は
            flg = False # フラグを折って
            break # 処理を中止する
    
    if(flg):
        print(xyn[i][0] - xym[0][0], xyn[i][1] - xym[0][1]) # 見つかったら平行移動分を表示してプログラムを終える
        exit()

10日目 AOJ ALDS1_5_A Exhaustive Search

C++

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

int main() {

    int n;
    cin >> n;

    vector<int> a(n);
    for(int i = 0; i < n; ++i) cin >> a[i];

    unordered_set<int> st; // aの組み合わせ足し算で作れる数字全てを保持する。高速で参照したいのでunordered_setを採用
    for(int i = 0; i < (1<<n); ++i){ // bit全探索、2進数で表した時1になるaを足す
        int sum = 0;
        for(int j = 0; j < n; ++j){
            if(i & (1<<j)) sum += a[j]; // 足す
        }
        st.insert(sum); // 作った数字を挿入、setなので重複は消える
    }

    int q;
    cin >> q;
    for(int _ = 0; _ < q; ++_){ // Python的な。idxに意味がないので _ にしてみました
        int m;
        cin >> m;

        if(st.count(m)) puts("yes"); // unoredered_setの検索は.count()が使いやすいと聞きました
        else puts("no");
    }

    return 0;
}

Python

n = int(input())
a = list(map(int, input().split()))

st = set() # aの組み合わせ足し算で作れる数字全てを保持する。Pythonのsetは早いぞ
for i in range(1<<n): # bit全探索、2進数で表した時1になるaを足す
    sm = 0
    for j in range(n):
        if(i & (1<<j)): sm += a[j] # 足す
        
    st.add(sm) # 作った数字を挿入、setなので重複は消える

q = int(input())
m = list(map(int, input().split()))

for i in range(q):
    if(m[i] in st): print('yes')
    else: print('no')

11日目〜

11日目 AtCoder Beginner Contest 128 C - Switches

C++

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int main() {
 
    int n, m;
    cin >> n >> m;
 
    vector<vector<int>> s(m); // 電球ごとに見るべきスイッチ、数が変わるので1軸目だけ決める
    vector<int> k(m); // 電球ごとに見るべきスイッチの数、あとでみたいので電球ごとに保持
    for(int i = 0; i < m; ++i){
        cin >> k[i];
        vector<int> sk(k[i]);
        for(int j = 0; j < k[i]; ++j) cin >> sk[j];
 
        s[i] = sk; // これのがpush_back(sk)より早いですか?
    }

    vector<int> p(m);
    for(int i = 0; i < m; ++i) cin >> p[i];
 
    int res = 0;
    for(int i = 0; i < (1<<n); ++i){ // n個のスイッチのon/off状態をbit全探索
        vector<int> on(n, 0); // スイッチの状態 off: 0
        for(int j = 0; j < n; ++j){
            if(i & (1<<j)) on[j] = 1; // bitが立っているところ→j番目が on: 1
        }
 
        bool all_on = true; // 全ての電球が点灯する時のみ守られるフラグ
        for(int j = 0; j < m; ++j){ // 電球について調べるので j < m(上と違う。。)
            int on_num = 0; // 点灯条件を愚直に実装
            for(int l = 0; l < k[j]; ++l){ // 見るべきスイッチの数k[j](kが既出のためl。。。)
                if(on[s[j][l] - 1]) ++on_num; // 見るべきスイッチがonならon_num(対象スイッチのon数)をインクリメント
            }
 
            if(on_num%2 != p[j]) { // そのまま実装「見るべきスイッチのonの個数を2で割ったあまりがp[j]のとき点灯」なのでこれを満たさない時
                all_on = false; // フラグが折れます
                break; // フラグが折れたらこれより先は見る必要なし
            }
        }
        
        if(all_on) ++res; // このスイッチのパターンで全て点灯、つまりフラグが守られたら結果をインクリメント
    }
 
    cout << res << endl;
 
    return 0;
}

Python

n, m = map(int, input().split())
 
ks = [list(map(int, input().split())) for _ in range(m)] # ひとまず行ごと全部受け取っちゃう
p = list(map(int, input().split()))
 
res = 0
for i in range(1<<n): # n個のスイッチのon/off状態をbit全探索
    on = [0 for _ in range(n)] # スイッチの状態 off: 0
    
    for j in range(n):
        if(i & (1<<j)): on[j] = 1 # bitが立っているところ→j番目が on: 1
    
    all_on = True # 全ての電球が点灯する時のみ守られるフラグ
    for j in range(m): # 電球について調べるので range(m)
        
        on_num = 0 # 点灯条件を愚直に実装
        for s in ks[j][1: ]: # 0番目は見るべきスイッチの数だが必要ないので1~ をみる
            if(on[s-1]): on_num += 1 # 見るべきスイッチがonならon_num(対象スイッチのon数)をインクリメント
 
        if(on_num%2 != p[j]): # そのまま実装「見るべきスイッチのonの個数を2で割ったあまりがp[j]のとき点灯」なのでこれを満たさない時
            all_on = False # フラグが折れます
            break # フラグが折れたらこれより先は見る必要なし
 
    if(all_on): res += 1 # このスイッチのパターンで全て点灯、つまりフラグが守られたら結果をインクリメント
 
print(res)

12日目 AtCoder Beginner Contest 002 D - 派閥

C++

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int main() {
 
    int n, m;
    cin >> n >> m;
 
    set<pair<int, int>> relations; // 大活躍のpair set あとで「知り合い関係があるか?」のチェックを高速に行う
    for(int i = 0; i < m; ++i) {
        int x, y;
        cin >> x >> y;
        --x; // 入力が1-indexなのでデクリメントしておく
        --y; // 同上
        relations.insert(make_pair(x, y)); // x < yの制約なので (x, y)の挿入で十分
    }
 
    int res = 0;
    for(int i = 1; i < (1<<n); ++i){ // 「n人がこの派閥に属するかどうか」をbit全探索する
        int num = 0; // その派閥の人数
        vector<int> faction; // 派閥の英語(今知りました)
        for(int j = 0; j < n; ++j){
            if(i & (1<<j)){
                faction.push_back(j); // bitが立ったらこの派閥のメンバー。factionにpushする
                ++num; // bitが立っているところを数えたらその派閥の人数になる(後からfaction.size()しても良いが・・)
            }
        }

        if(num < res) continue; // 派閥の人数が暫定トップに満たなければ以下の処理は必要なし
 
        bool flg = true; // 派閥の条件を満たす時守られるフラグ
        for(int j = 0; j < num-1; ++j){ // 派閥のメンバー間の関係を全探索
            for(int k = j+1; k < num; ++k){
                pair<int, int> relation = make_pair(faction[j], faction[k]); // あるメンバー間の関係pair
 
                if(relations.find(relation) == relations.end()) { // が、知り合いsetの中に存在しなければ
                    flg = false; // フラグを折って
                    break; // 処理を終える
                }
            }
            if(!flg) break; // 二重ループなので、こちらでも終わらせておく
        }
 
        if(flg) res = max(res, num); // この派閥が存在すれば暫定トップと戦う
 
    }
 
    cout << res << endl;
 
    return 0;
}

Python

n, m = map(int, input().split())

relations = set() # 大活躍の tuple set あとで「知り合い関係があるか?」のチェックを高速に行う
for i in range(m):
    x, y = map(int, input().split())
    x -= 1 # 入力が1-indexなのでデクリメントしておく
    y -= 1 # 同上
    
    relations.add((x, y)) # x < yの制約なので (x, y)の挿入で十分

res = 0
for i in range(1, 1<<n): # 「n人がこの派閥に属するかどうか」をbit全探索する
    num = 0 # その派閥の人数
    faction = [] # 派閥の英語(さっき知りました)
    for j in range(n):
        if(i & (1<<j)): 
            faction.append(j) # bitが立ったらこの派閥のメンバー。factionにappendする
            num += 1 # bitが立っているところを数えたらその派閥の人数になる(後からlen(faction)しても良いが・・)
    
    if(num < res): continue # 派閥の人数が暫定トップに満たなければ以下の処理は必要なし
    
    flg = True # 派閥の条件を満たす時守られるフラグ
    for j in range(num-1): # 派閥のメンバー間の関係を全探索
        for k in range(j+1, num):
            relation = faction[j], faction[k] # あるメンバー間の関係tuple
            
            if(relation not in relations): # が、知り合いsetの中に存在しなければ
                flg = False # フラグを折って
                break # 処理を終える
        
        if(not flg): break # 二重ループなので、こちらでも終わらせておく
    
    if(flg): res = max(res, num) # この派閥が存在すれば暫定トップと戦う
        
print(res)

13日目 第7回日本情報オリンピック 予選(オンライン) E - おせんべい

C++

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int main() {
 
    int r, c;
    cin >> r >> c;
    vector<vector<int>> a(r, vector<int>(c)); // 盤面
    vector<int> black(c, 0); // その「列」の黒いおせんべいの数を持つことでO(c)で出荷可能枚数の最大値を求める
    for(int i = 0; i < r; ++i){
        for(int j = 0; j < c; ++j){
            cin >> a[i][j];
            black[j] += a[i][j]; // 白は0 黒は1なのでそのまま足せば黒の枚数になる
        }
    }
 
    int res = 0; // 最初はどの行も裏返さなかった場合の出荷可能枚数を調べる(bit全探索のループを0からにしても良いが・・)
    for(int i = 0; i < c; ++i){
        res += max(black[i], r - black[i]); // 任意の列を裏返せるので「max(裏返した時の黒枚数, 裏返さなかった時の黒枚数)」が「その列の出荷可能枚数の最大値」になる
    }
 
    for(int i = 1; i < (1<<r); ++i){ // 「ある列を裏返すかどうか」をbit全探索
        vector<int> black_diff(c, 0); // その「列」の黒枚数がこの操作どれだけ変わるか
        for(int j = 0; j < r; ++j){
 
            if(i & (1<<j)){
                for(int k = 0; k < c; ++k){
                    black_diff[k] += a[j][k] ? -1: 1; // 元々黒なら−1、白なら+1を加算
                }
            }
        }
 
        int sum = 0;
        for(int j = 0; j < c; ++j){
            sum += max(black[j]+black_diff[j], r-(black[j]+black_diff[j])); // どれだけ変わるかを反映させて同じように出荷可能枚数の最大値を計算する
        }
        
        res = max(res, sum); // 暫定トップと戦う
    }
 
    cout << res << endl;
 
    return 0;
}

Python

r, c = map(int, input().split())
a = [list(map(int, input().split())) for _ in range(r)] # 盤面
 
black = [0 for _ in range(c)] # その「列」の黒いおせんべいの数を持つことでO(c)で出荷可能枚数の最大値を求める
for i in range(r):
    for j in range(c):
        black[j] += a[i][j]
 
res = 0 # 最初はどの行も裏返さなかった場合の出荷可能枚数を調べる(bit全探索のループを0からにしても良いが・・)
for i in range(c):
    res += max(black[i], r - black[i]) # 任意の列を裏返せるので「max(裏返した時の黒枚数, 裏返さなかった時の黒枚数)」が「その列の出荷可能枚数の最大値」になる
 
for i in range(1<<r): # 「ある列を裏返すかどうか」をbit全探索
    black_diff = [0 for _ in range(c)] # その「列」の黒枚数がこの操作どれだけ変わるか
    
    for j in range(r):
        
        if(i & (1<<j)):
            for k in range(c): # 元々黒なら−1、白なら+1を加
                if(a[j][k]): black_diff[k] -= 1
                else: black_diff[k] += 1
    
    sm = 0
    for j in range(c):
        sm += max(black[j]+black_diff[j], r-(black[j]+black_diff[j])) # どれだけ変わるかを反映させて同じように出荷可能枚数の最大値を計算する
 
    res = max(res, sm) # 暫定トップと戦う
 
print(res)

こちら、ツイートでは余裕wwなどと言っていますがPythonだと間に合いません・・PyPyのが10倍くらい早いのでそちらで

14日目 square869120Contest #4 B - Buildings are Colorful!

C++

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int main() {
 
    int n, k;
    cin >> n >> k;
 
    vector<ll> a(n);
    for(int i = 0; i < n; ++i) cin >> a[i];
 
    ll res = 1LL << 60; // 最小値が知りたいので答えの初期値はでかい数で
    for(int i = 0; i < (i<<n); ++i){ // k 個のビルが条件を満たすとし、その選び方をbit全探索する。選んだビル数がk以外の時はcontinueという方法で
 
        if(i%2 == 0) continue; // 先頭のビルを見ないのは不可能なので一つ目のbitは必ず立っている必要がありその必要十分条件は「奇数であること」(最初のbitが1)
 
        vector<int> use_idx(n, 0); // どのビルに着目するか 0: 無視
        int use_cnt = 0; // 着目するビルの数 kになる時だけ考える
        for(int j = 0; j < n; ++j){
            if(i & (1<<j)){
                use_idx[j] = 1; // 1: 着目
                ++use_cnt; // ++着目
            }
        }
        if(use_cnt != k) continue; // k個のビルに着目することだけを考える
 
        ll sum = 0; // 一つ目、先頭。いじる必要はないのでコストも0
        ll mx = a[0]; // 先頭の高さが暫定最大の高さ
        for(int j = 1; j < n; ++j){ // 二つ目からみていく
            if(use_idx[j]){ // このビルが着目するビルならば
                
                if(a[j] <= mx) { // もし暫定最大の高さがこのビル以上の高さならば
                    sum += mx+1 - a[j]; // このビルは最大+1の高さまで最低伸ばす必要があり
                    ++mx; // その高さが暫定最大の高さになる
                }
            }
 
            mx = max(mx, a[j]); // 着目するビルかどうかにかかわらず暫定最大高さは更新される(これをif(use)内に書くとだめだよ。見ないからと言って透明にはならないので)
        }
 
        res = min(res, sum); // 全てのコストを計算したら暫定最安コストと戦う
    }
 
    cout << res << endl;
 
    return 0;
}

Python

n, k = map(int, input().split())
a = list(map(int, input(). split()))

res = 1 << 60 # 最小値が知りたいので答えの初期値はでかい数で
for i in range(1<<n): # k 個のビルが条件を満たすとし、その選び方をbit全探索する。選んだビル数がk以外の時はcontinueという方法で
    if(i%2 == 0): continue # 先頭のビルを見ないのは不可能なので一つ目のbitは必ず立っている必要がありその必要十分条件は「奇数であること」(最初のbitが1)
    
    use_idx = [0 for _ in range(n)] # どのビルに着目するか 0: 無視
    use_cnt = 0 # 着目するビルの数 kになる時だけ考える
    for j in range(n):
        if(i & (1<<j)):
            use_idx[j] = 1 # 1: 着目
            use_cnt += 1 # ++着目
    
    if(use_cnt != k): continue # k個のビルに着目することだけを考える
    
    sm = 0 # 一つ目、先頭。いじる必要はないのでコストも0
    mx = a[0] # 先頭の高さが暫定最大の高さ
    for j in range(1, n):
        
        if(use_idx[j]): # 二つ目からみていく
            if(a[j] <= mx): # もし暫定最大の高さがこのビル以上の高さならば
                sm += mx+1 - a[j] # このビルは最大+1の高さまで最低伸ばす必要があり
                mx += 1 # その高さが暫定最大の高さになる
        
        mx = max(mx, a[j]) # 着目するビルかどうかにかかわらず暫定最大高さは更新される(これをif(use)内に書くとだめだよ。見ないからと言って透明にはならないので)
    
    res = min(res, sm) # 全てのコストを計算したら暫定最安コストと戦う

print(res)

15日目 AtCoder Beginner Contest 145 C - Average Length

C++

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

// ある座標とある座標の距離を計算して返す関数。
double calc_dist(pair<double, double> current, pair<double, double> nxt){
    return pow((nxt.first - current.first) * (nxt.first - current.first) + (nxt.second - current.second) * (nxt.second - current.second), 0.5);
}
 
int main() {
 
    int n;
    cin >> n;
 
    vector<pair<double, double>> xy(n); // next_permutaionを使いたいのでvectorに座標ペアを入れた。
    for(int i = 0; i < n; ++i){ // x(n), y(n)のように別で持つ場合はインデックスの順列を作れば良さそう
        double x, y;
        cin >> x >> y;
        xy[i] = make_pair(x, y);
    }
    sort(xy.begin(), xy.end()); // ここ重要です。「next_permutationは辞書順で次の順列を作る」ため、予め辞書順で一番すなわち昇順のソートをしておく必要がある。
 
    int n_fact = 1; // 組み合わせがn!通りなので平均値を出すためにはn!で割りたい→事前に計算しておく。
    for(int i = 1; i <= n; ++i) n_fact *= i; // そのままの実装。
    
    double sum = 0.0; // 全ての組み合わせの距離をここに足していって、最後にn!で割る
     
    do{ // 訪れる座標の順番を順列全探索する
        pair<double, double> current = xy[0]; // この選び方における1番目の座標。始点にする
        double dist = 0.0; // この選び方における合計距離
        for(int i = 1; i < n; ++i){
            pair<double, double> nxt = xy[i]; // 行先の座標。
            dist += calc_dist(current, nxt); // 距離を計算して合計距離に足す。
            current = nxt; // 行先を始点にして次の座標へ
        }
 
        sum += dist; // この選び方の合計距離を全体の合計距離に足す
 
    } while(next_permutation(xy.begin(), xy.end()));
 
    printf("%.10f\n", sum / n_fact); // 小数表示。入力がcinなのにprintf()はきもいですか?
 
    return 0;
}

Python

from itertools import permutations # 順列全探索のためitertoolsのpermutationsをimport

n = int(input())
xy = [list(map(int, input().split())) for _ in range(n)] # C++のnext_permutationと異なり辞書順で次を作るわけでないのでソートは不要

n_fact = 1 # 組み合わせがn!通りなので平均値を出すためにはn!で割りたい→事前に計算しておく。
for i in range(1, n+1): n_fact *= i # そのままの実装。

sm = 0 # 全ての組み合わせの距離をここに足していって、最後にn!で割る
for itr in permutations(xy): # 訪れる座標の順番を順列全探索する
    x, y = itr[0] # この選び方における1番目の座標。始点にする
    dist = 0 # この選び方における合計距離
    for nx, ny in itr[1: ]: # 組み合わせの2番目の要素以降の処理、中身をそのままnx, nyに入れる
        dist += ((nx-x)**2 + (ny-y)**2) ** 0.5 # Pythonならではの記法?
        x, y = nx, ny # これも。一気にcurrentにnxtを渡せる

    sm += dist # この選び方の合計距離を全体の合計距離に足す

print(sm / n_fact) # 何もしなくても少数で出てくれます

おわりに

どんどん難しくなりそうで震えています。

20
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?