ルクの競プロ部屋

学生の競プロ生活を見たい人はどぞ

Yukicoder灰<No.24A>

問題

問題はこちら
問題文↓

4つの数を聞き、含まれていたらYES、でなければNOを返す数あてゲームをします
必ず数字が1つに定まる時、ある数は何かを出力してください

解法

YESのときは、聞いた数字4つ以外の数字を0にする
NOのときは、聞いた数字4つの数字を0にする
そうすると、必ずある数が0でなくなる(詳しく言うと、1または-1となる)ので、それを探す

#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (ll i = 0; i < (n); ++i)
#define rep1(i, n) for (ll i = 1; i < (n); ++i)
#define rrep(i, n) for (ll i = n; i > 0; --i)
#define bitrep(i, n) for (ll i = 0; i < (1 << n); ++i)
#define all(a) (a).begin(), (a).end()
#define rall(a) (a).rbegin(), (a).rend()
#define yesNo(b) ((b) ? "Yes" : "No")
using ll = long long;
using ull = unsigned long long;
using ld = long double;
string alphabet = "abcdefghijklmnopqrstuvwxyz";
string ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const double pi = 3.141592653589793;
int smallMOD = 998244353;
int bigMOD = 1000000007;

int main()
{
    int n;
    cin >> n;
    vector<int> A(10, -1);
    rep(i, n)
    {
        int a, b, c, d;
        string s;
        cin >> a >> b >> c >> d >> s;
        if (s == "YES")
        {
            rep(j, 10)
            {
                if (j != a && j != b && j != c && j != d)
                    A[j] = 0;
                else if (A[j] == -1)
                    A[j] = 1;
            }
        }
        else
        {
            A[a] = 0;
            A[b] = 0;
            A[c] = 0;
            A[d] = 0;
        }
    }
    int idx = 0;
    if (count(all(A), 1) == 0)
    {
        rep(i, 10)
        {
            if (A[i] == -1)
                idx = i;
        }
    }
    else
    {
        rep(i, 10)
        {
            if (A[i] == 1)
                idx = i;
        }
    }
    cout << idx << endl;
    return 0;
}

Yukicoder灰<No.21A>

問題

問題はこちら
問題文↓

N個の数字が与えられるので、これをK個のグループに振り分ける
グループごとに平均を計算し, それらをもとに 最大の平均 - 最小の平均 を計算し、
最後に小数点以下を切り上げその値を「平均の差」と呼ぶ。
平均の差を最も大きくするようなグループ分けをしたとき、平均の差はいくつになるか答えよ。

解法

難しいことがごちゃごちゃ書いているが、
結局は{{最大の要素},{...},{...},{...},{最小の要素}}と分ければ必ず平均の差は最大の要素-最小の要素となり、
これが答えとなる

#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (ll i = 0; i < (n); ++i)
#define rep1(i, n) for (ll i = 1; i < (n); ++i)
#define rrep(i, n) for (ll i = n; i > 0; --i)
#define bitrep(i, n) for (ll i = 0; i < (1 << n); ++i)
#define all(a) (a).begin(), (a).end()
#define rall(a) (a).rbegin(), (a).rend()
#define yesNo(b) ((b) ? "Yes" : "No")
using ll = long long;
using ull = unsigned long long;
using ld = long double;
string alphabet = "abcdefghijklmnopqrstuvwxyz";
string ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const double pi = 3.141592653589793;
int smallMOD = 998244353;
int bigMOD = 1000000007;

int main()
{
    int n, a;
    cin >> n >> a;
    vector<int> A(n);
    rep(i, n) cin >> A[i];
    sort(all(A));
    cout << (A.back() - A[0]) << endl;
    return 0;
}

Yukicoder灰<No.18A>

問題

問題はこちら
問題文↓

シーザ暗号の拡張版を思いつきました
i文字目の文字をi回右にずらすという操作
すなわちヴィジュネル暗号みたいなことをしてください

解法

ASCIIコードに任せましょう
'A'-65=0
'B'-65=1
...
'Z'-65=25のようになるので、それを全ての文字について行うだけです

#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (ll i = 0; i < (n); ++i)
#define rep1(i, n) for (ll i = 1; i < (n); ++i)
#define rrep(i, n) for (ll i = n; i > 0; --i)
#define bitrep(i, n) for (ll i = 0; i < (1 << n); ++i)
#define all(a) (a).begin(), (a).end()
#define rall(a) (a).rbegin(), (a).rend()
#define yesNo(b) ((b) ? "Yes" : "No")
using ll = long long;
using ull = unsigned long long;
using ld = long double;
string alphabet = "abcdefghijklmnopqrstuvwxyz";
string ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const double pi = 3.141592653589793;
int smallMOD = 998244353;
int bigMOD = 1000000007;

int main()
{
    string s;
    cin >> s;
    rep(i, s.size())
    {
        cout << ALPHABET[(s[i] - 65 - (i + 1) % 26 + 26) % 26];
    }
    cout << endl;
    return 0;
}

Yukicoder灰<No.5A>

問題

問題はこちら
問題内容↓

最大L個のブロックが入る箱がある
ブロックの塊があるからそれらの塊がいくつ入るかを調べてください

解法

ソートして小さいものから詰めましょう

#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (ll i = 0; i < (n); ++i)
#define rep1(i, n) for (ll i = 1; i < (n); ++i)
#define rrep(i, n) for (ll i = n; i > 0; --i)
#define bitrep(i, n) for (ll i = 0; i < (1 << n); ++i)
#define all(a) (a).begin(), (a).end()
#define rall(a) (a).rbegin(), (a).rend()
#define yesNo(b) ((b) ? "Yes" : "No")
using ll = long long;
using ull = unsigned long long;
using ld = long double;
string alphabet = "abcdefghijklmnopqrstuvwxyz";
string ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const double pi = 3.141592653589793;
int smallMOD = 998244353;
int bigMOD = 1000000007;

int main()
{
    int l, n;
    cin >> l >> n;
    vector<int> A(n);
    rep(i, n) cin >> A[i];
    sort(all(A));
    int ans = 0, cnt = l;
    rep(i, n)
    {
        cnt -= A[i];
        if (cnt < 0)
            break;
        ans = i + 1;
    }
    cout << ans << endl;
    return 0;
}

競プロ精選 100 問 by C++<二分探索>

はじめに

皆さんこんにちは!
二分探索の基礎はできるから、応用問題を頑張るぞい!
精選100問はこちら

18 ALDS_4_B - 二分探索

問題はこちらです!
問題文↓

main配列とsub配列が与えられるので、sub配列の要素がmain配列にいくつ含まれるかをカウントしてください

個数が大きいので、2分探索しましょうという問題ですね!
以下のようになりました

#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (ll i = 0; i < (n); ++i)
#define bitrep(i, n) for (ll i = 0; i < (1 << n); ++i)
#define all(a) (a).begin(), (a).end()
#define rall(a) (a).rbegin(), (a).rend()
#define yesNo(b) ((b) ? "Yes" : "No")
using ll = long long;
using ull = unsigned long long;
string alphabet = "abcdefghijklmnopqrstuvwxyz";
string ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const double pi = 3.141592653589793;
int smallMOD = 998244353;
int bigMOD = 1000000007;

int main()
{
    int n;
    cin >> n;
    vector<int> A(n);
    rep(i, n) cin >> A[i];
    int m;
    cin >> m;
    int ans = 0;
    rep(i, m)
    {
        int x;
        cin >> x;
        if (x == A[lower_bound(all(A), x) - A.begin()])
            ans++;
    }
    cout << ans << endl;
    return 0;
}

STLにはlower_boundがあるので、それを活用しましょう!

19 JOI 2009 本選 2 - ピザ

問題はこちらです!
問題文↓

ピザ屋と宅配先は円状に並んでいる
いくつかのピザ屋と宅配先の座標が与えられるので一番近いピザ屋からピザを届けて、最も小さいコストでピザを配達してください
コストは、距離とします

宅配先から一番近いピザ屋をコーナーケースに注意しながら二分探索していけばいいですね!
宅配先が0のときと、一番遠い場所のピザ屋よりも奥のときには注意です
宅配先が0のときはコストが必ず0になり、一番遠い場所のピザ屋よりも奥のときは0とも比較しないといけません
以下のようになりました

#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (ll i = 0; i < (n); ++i)
#define rrep(i, n) for (ll i = n; i > 0; --i)
#define bitrep(i, n) for (ll i = 0; i < (1 << n); ++i)
#define all(a) (a).begin(), (a).end()
#define rall(a) (a).rbegin(), (a).rend()
#define yesNo(b) ((b) ? "Yes" : "No")
using ll = long long;
using ull = unsigned long long;
using ld = long double;
string alphabet = "abcdefghijklmnopqrstuvwxyz";
string ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const double pi = 3.141592653589793;
int smallMOD = 998244353;
int bigMOD = 1000000007;

int main()
{
    // input.
    ll d, n, m;
    cin >> d >> n >> m;
    vector<ll> D(n, 0);
    vector<ll> K(m);
    rep(i, n - 1) cin >> D[i + 1]; // はじめは0(本店がある).
    rep(i, m) cin >> K[i];

    // 二分探索をするので、前処理(ソート+同じ座標の削除).
    sort(all(D));
    sort(all(K));
    D.erase(unique(all(D)), D.end());

    ll ans = 0;
    rep(i, m)
    {
        if (K[i] == 0) // もし0へ配達するなら.
        {
            // 本店から運ぶのてコスト0.
            ans += 0;
        }
        else if (K[i] > D.back()) // もし、一番大きい座標のピザ屋よりも先の宅配先なら、0と一番大きい座標の比較
        {
            ans += min((d - K[i]), (K[i] - D.back()));
        }
        else
        {
            ans += min(K[i] - D[lower_bound(all(D), K[i]) - D.begin() - 1], D[lower_bound(all(D), K[i]) - D.begin()] - K[i]);
        }
    }
    cout << ans << endl;
    return 0;
}

同じ座標の削除は必要なのだろうか、、、?

20 AtCoder Beginner Contest 077 C - Snuke Festival

問題はこちらです!
問題文↓

3つのアイテムを選んで順番に並べさせたとき、階段状に大きくなるようにしたい
選び方は何通りあるか
ただし、個数はとても多いものとする

真ん中のアイテムを固定すると、上と下のアイテムの個数がわかります!
このような感じになりました

#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (ll i = 0; i < (n); ++i)
#define rrep(i, n) for (ll i = n; i > 0; --i)
#define bitrep(i, n) for (ll i = 0; i < (1 << n); ++i)
#define all(a) (a).begin(), (a).end()
#define rall(a) (a).rbegin(), (a).rend()
#define yesNo(b) ((b) ? "Yes" : "No")
using ll = long long;
using ull = unsigned long long;
using ld = long double;
string alphabet = "abcdefghijklmnopqrstuvwxyz";
string ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const double pi = 3.141592653589793;
int smallMOD = 998244353;
int bigMOD = 1000000007;

int main()
{
    // 入力.
    ll n;
    cin >> n;
    vector<ll> A(n), B(n), C(n);
    rep(i, n) cin >> A[i];
    rep(i, n) cin >> B[i];
    rep(i, n) cin >> C[i];

    // 二分探索するのでソート.
    sort(all(A));
    sort(all(B));
    sort(all(C));

    // 真ん中のアイテムを全探索して、上には小さいアイテム、下には大きいアイテムの個数を数える.
    ll ans = 0;
    rep(i, n)
    {
        ll top = (lower_bound(all(A), B[i]) - A.begin());
        ll bottom = (n - (upper_bound(all(C), B[i]) - C.begin()));
        ans += top * bottom;
    }
    cout << ans << endl;
    return 0;
}

二分探索って気づいたら簡単ですね

21 AtCoder Beginner Contest 023 D - 射撃王

問題はこちらです!
問題文↓

いくつかの風船が飛んでおり、これらを全て撃ち抜く
撃ち抜いた風船の高度がコストとなる
風船のはじめの高度と毎秒の速度が与えられるので、コストをできる限り最低にしたい
この最小値を求めてください

全ての風船を高度x以内に消すことが出来るかを考えると二分探索をすれば良いことがわかる!
コードはこんな感じになりました

#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (ll i = 0; i < (n); ++i)
#define rrep(i, n) for (ll i = n; i > 0; --i)
#define bitrep(i, n) for (ll i = 0; i < (1 << n); ++i)
#define all(a) (a).begin(), (a).end()
#define rall(a) (a).rbegin(), (a).rend()
#define yesNo(b) ((b) ? "Yes" : "No")
using ll = long long;
using ull = unsigned long long;
using ld = long double;
string alphabet = "abcdefghijklmnopqrstuvwxyz";
string ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const double pi = 3.141592653589793;
int smallMOD = 998244353;
int bigMOD = 1000000007;

int main()
{
    // 入力.
    ll n;
    cin >> n;
    vector<pair<ll, ll>> Balloons(n);
    rep(i, n) cin >> Balloons[i].first >> Balloons[i].second;

    // 二分探索をする.
    ll left = 0, right = INT64_MAX;
    while (right > left + 1)
    {
        ll x = (left + right) / 2;

        // 全ての風船が高さxで破壊しきることが可能か.
        bool f = true;
        vector<ll> times;
        rep(i, n)
        {
            if (Balloons[i].first > x)
            {
                // もし風船の初期高度がxを超えていたらアウト.
                f = false;
            }
            else
            {
                // 何秒まで破壊しなくてもいいか.
                times.push_back((x - Balloons[i].first) / Balloons[i].second);
            }
        }

        // 破壊しなくては行けない順番にソート.
        sort(all(times));
        rep(i, times.size())
        {
            if (times[i] < i)
                f = false;
        }
        if (f)
            right = x;
        else
            left = x;
    }

    // 結果を出力する.
    cout << right << endl;
    return 0;
}

22 AtCoder Regular Contest 054 B - ムーアの法則

問題はこちらです!
問題文↓

ある現代のコンピュータではp年かかる計算があります
1年おきにコンピュータの計算の速さが{2}^{x/1.5}倍になるという法則に従えば、一番短くて何年で実行を終わることができますか?
もちろん計算中にコンピュータを変えることはできないです

これは、めちゃくちゃいい問題でした!
まずは立式するところから始めます
仕事のスピードが速くなる=反比例となるって考えれば、x年後にy年かかるという式は以下になります
$f(x)=\frac{1}{{2}^{\frac{x}{1.5}}}a+x$ グラフを書くとこんな感じ...
いい感じな下に凸のグラフなので、これの最小値を求めます!
最小値を求めるには式を微分して、0を代入して解くといった感じですね
微分はwolfram君にでも投げましょう
すると、以下のように帰ってきました
$f'(x)=1-\frac{1}{3}a \times 2^{1-x} \times \sqrt[3]{{2}^{x}} \times log(2)$(ここでのlogの底はe)
これに$f'(x)=0$となるようにxを解くのが極小となる
xを解くと、$x=\frac{3(log(2)-log(\frac{3}{a log(2)}))}{2log(2)}$となる!
グラフはこんな感じ...

確かに、極小のx座標になってますね!
あとはこのxをf(x)に代入したら、最小値を求められますね
しかし、x<0のときはそのまま出力することを忘れずに、、、
こんなコードになってO(1)で解けました!

#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (ll i = 0; i < (n); ++i)
#define rrep(i, n) for (ll i = n; i > 0; --i)
#define bitrep(i, n) for (ll i = 0; i < (1 << n); ++i)
#define all(a) (a).begin(), (a).end()
#define rall(a) (a).rbegin(), (a).rend()
#define yesNo(b) ((b) ? "Yes" : "No")
using ll = long long;
using ull = unsigned long long;
using ld = long double;
string alphabet = "abcdefghijklmnopqrstuvwxyz";
string ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const double pi = 3.141592653589793;
int smallMOD = 998244353;
int bigMOD = 1000000007;

int main()
{
    ld p;
    cin >> p;
    ld x = (3 * (logl(2) - logl(3 / (p * logl(2))))) / (2 * logl(2));
    if (x < 0)
        cout << p << endl;
    else
        cout << setprecision(12) << x + 1 / powl(2, 2 * x / 3) * p << endl;
    return 0;
}

微分良いね(手計算とは言っていない)

23 JOI 2008 本選 3 - ダーツ

問題はこちらです!
問題文↓

N個の的があり、ポイントが書かれています
最大4本のダーツを使用して、あたった的の点数の合計があなたの得点です
ただし、得点がM点を超えた場合、あなたの得点は0点になります
さて、獲得できるポイントの最大は何点でしょう

普通にすると、O(N⁴)かかるので駄目です
しかし、N≦1000なので、O(N²)にすれば間に合いそうです そこで、半分全列挙をして、いい組み合わせを二分探索で見つけて上げればいいです!
こんな感じのコードになりました

#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (ll i = 0; i < (n); ++i)
#define rrep(i, n) for (ll i = n; i > 0; --i)
#define bitrep(i, n) for (ll i = 0; i < (1 << n); ++i)
#define all(a) (a).begin(), (a).end()
#define rall(a) (a).rbegin(), (a).rend()
#define yesNo(b) ((b) ? "Yes" : "No")
using ll = long long;
using ull = unsigned long long;
using ld = long double;
string alphabet = "abcdefghijklmnopqrstuvwxyz";
string ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const double pi = 3.141592653589793;
int smallMOD = 998244353;
int bigMOD = 1000000007;

int main()
{
    // 入力.
    ll n, m;
    cin >> n >> m;
    vector<ll> A(n + 1, 0);
    rep(i, n) cin >> A[i + 1];
    vector<ll> B;
    rep(i, n + 1)
    {
        rep(j, n - i + 1)
        {
            B.push_back(A[i] + A[i + j]);
        }
    }

    // 被りを削除.
    sort(all(B));
    B.erase(unique(all(B)), B.end());

    // 半分全列挙したもので、二分探索.
    ll ans = 0;
    rep(i, B.size())
    {
        if (upper_bound(all(B), m - B[i]) - B.begin() - 1 > -1)
            ans = max(ans, m - (m - B[i] - B[(upper_bound(all(B), m - B[i]) - B.begin() - 1)]));
    }
    cout << ans << endl;
    return 0;
}

だいぶ簡単な問題でした!

終わりに

最後まで読んでくださりありがとうございました
ではまた!

競プロ精選 100 問 by C++<全探索:順列全探索>

はじめに...

全探索編最後の章です!
順列全探索の3問やりました!
精選100問はこちら

15 AtCoder Beginner Contest 145 C - Average Length

問題はこちらです!
問題文↓

N個の街の座標が与えられる
街をすべて巡るのにはN!通りの方ほうがあることは自明
すべての街のめぐり方を取ったときの距離の平均を求めてください
距離の式はsqrt((x_i-x_j)^2+(y_i-y_j)^2)とします

例えば、N=3のとき
(1,2,3),(1,3,2),(2,1,3),(2,3,1),(3,1,2),(3,2,1)の6通りをすべて書き出すことが出来たら良い!
C++でこういうときはnext_permutationを使う!
2≦n≦8だから8!で間に合うのでこんな感じになりました!

#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (ll i = 0; i < (n); ++i)
#define bitrep(i, n) for (ll i = 0; i < (1 << n); ++i)
#define all(a) (a).begin(), (a).end()
#define rall(a) (a).rbegin(), (a).rend()
#define yesNo(b) ((b) ? "Yes" : "No")
using ll = long long;
using ull = unsigned long long;
string alphabet = "abcdefghijklmnopqrstuvwxyz";
string ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const double pi = 3.141592653589793;
int smallMOD = 998244353;
int bigMOD = 1000000007;

int main()
{
    int n;
    cin >> n;
    vector<pair<int, int>> M(n);
    rep(i, n) cin >> M[i].first >> M[i].second;
    double ans = 0;
    int x = 0;
    sort(all(M));// 忘れずにソートする.
    // 順列全探索!!!.
    do
    {
        double c = 0;
        for (int i = 0; i < M.size() - 1; i++)
        {
            c += sqrtl((M[i].first - M[i + 1].first) * (M[i].first - M[i + 1].first) + (M[i].second - M[i + 1].second) * (M[i].second - M[i + 1].second));
        }
        ++x;
        ans += c;
    } while (next_permutation(all(M)));
    cout << setprecision(11) << ans / x << endl;
    return 0;
}

こういうのはスニペット登録しとく!

16 AtCoder Beginner Contest 150 C - Count Order

問題はこちらです!
問題文↓

1からNまでのリストを辞書順に全列挙してほしい
そこで、2つのリストを与えるので、辞書順で何番目と何番目になるかを教えてください
ただし、解答方法は2つのリストの番号の差の絶対値にしてください

順列全探索をするだけ!

#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (ll i = 0; i < (n); ++i)
#define bitrep(i, n) for (ll i = 0; i < (1 << n); ++i)
#define all(a) (a).begin(), (a).end()
#define rall(a) (a).rbegin(), (a).rend()
#define yesNo(b) ((b) ? "Yes" : "No")
using ll = long long;
using ull = unsigned long long;
string alphabet = "abcdefghijklmnopqrstuvwxyz";
string ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const double pi = 3.141592653589793;
int smallMOD = 998244353;
int bigMOD = 1000000007;

template <typename T>
bool vectorEqual(vector<T> const &v1, vector<T> const &v2)
{
    return (v1.size() == v2.size() && equal(v1.cbegin(), v1.cend(), v2.cbegin()));
}

int main()
{
    int n;
    cin >> n;
    vector<int> A(n), B(n), C(n);
    rep(i, n) A[i] = i + 1;
    rep(i, n) cin >> B[i];
    rep(i, n) cin >> C[i];
    int x = 0;
    int b, c;
    do
    {
        ++x;
        if (vectorEqual(A, B))
            b = x;
        if (vectorEqual(A, C))
            c = x;
    } while (next_permutation(all(A)));
    cout << abs(b - c) << endl;
    return 0;
}

1からNまでのリストAの順列を全列挙して、B・Cに当てはまるときがあったらメモしておく
最後に差の絶対値を取ったら終了!

17 ALDS_13_A - 8 クイーン問題

問題はこちらです!
問題文↓

角と飛車を合わしたような動きをするクイーンがいる
このクイーンを8×8のチェス盤に8個並べる時、どのクイーン同士も取られない置き方にしたい
初めの数個は置く位置を指定して、解は1つだけに絞れるようにするので、このただ1つの解を求めてください

メッチャクチャ時間かかりました
前の回で使った組み合わせ全列挙と深さ優先探索をすることで解くことが可能です!
多分、順列全探索で解けるのだろうけど思いつかない、、、
結果こんな感じになりました

#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (ll i = 0; i < (n); ++i)
#define bitrep(i, n) for (ll i = 0; i < (1 << n); ++i)
#define all(a) (a).begin(), (a).end()
#define rall(a) (a).rbegin(), (a).rend()
#define yesNo(b) ((b) ? "Yes" : "No")
using ll = long long;
using ull = unsigned long long;
string alphabet = "abcdefghijklmnopqrstuvwxyz";
string ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const double pi = 3.141592653589793;
int smallMOD = 998244353;
int bigMOD = 1000000007;

vector<pair<int, int>> M;
vector<pair<int, int>> N;
int x = 0;
int y = 0;
bool f = false;

void recursive_comb(vector<int> indexes, int s, int rest, function<void(vector<int>)> f)
{
    if (rest == 0)
    {
        f(indexes);
    }
    else
    {
        if (s < 0)
            return;
        recursive_comb(indexes, s - 1, rest, f);
        indexes[rest - 1] = s;
        recursive_comb(indexes, s - 1, rest - 1, f);
    }
}

void foreach_comb(int n, int k, function<void(vector<int>)> f)
{
    vector<int> indexes(k);
    recursive_comb(indexes, n - 1, k, f);
}

// どのクイーン同士も取られない置き方か.
bool isHit()
{
    N.resize(M.size());
    copy(all(M), N.begin());
    f = false;
    auto lambda = [](vector<int> indexes)
    {
        f |= (N[indexes[0]].first == N[indexes[1]].first ||
              N[indexes[0]].second == N[indexes[1]].second ||
              abs(N[indexes[0]].first - N[indexes[1]].first) == abs(N[indexes[0]].second - N[indexes[1]].second));
    };
    foreach_comb(N.size(), 2, lambda);
    return f;
}

// xがMに入っているか.
bool xInM(int x)
{
    bool flag = false;
    rep(i, M.size())
    {
        flag |= (M[i].first == x);
    }
    return flag;
}

// 再帰的にQueenを設置する.
void setQueen()
{
    // もし、xが最後まで行ったら終了.
    if (x == 8)
    {
        return;
    }
    // もうすでにxが存在していたらパス.
    if (xInM(x))
    {
        ++x;
    }
    else
    {
        M.push_back({x, y});
        // もし、どのクイーン同士も取られない置き方だったら次に行く.
        if (!isHit())
        {
            ++x;
            y = 0;
        }
        else
        {
            ++y;
            M.pop_back();
            while (y == 8)
            {
                // どんどん遡る.
                x = M.back().first;
                y = M.back().second + 1;
                M.pop_back();
            }
        }
    }
    // DFSのループ.
    setQueen();
}

int main()
{
    // input.
    int n;
    cin >> n;
    rep(i, n)
    {
        int x, y;
        cin >> x >> y;
        M.push_back(pair(x, y));
    }

    // 深さ優先探索.
    setQueen();

    // 結果を作成する.
    vector<string> ans(8, string(8, '.'));
    rep(i, M.size())
    {
        ans[M[i].first][M[i].second] = 'Q';
    }

    // 出力.
    rep(i, 8)
    {
        cout << ans[i] << endl;
    }
    return 0;
}

2時間ぐらい格闘しましたよ、、、

最後に...

最後までお疲れ様でした! ではまた!

競プロ精選 100 問 by C++<全探索:ビット全探索>

はじめに...

今日は、bit全探索5問やっていきまーす
精選100問はこちら

10 ALDS_5_A - 総当たり

問題はこちらです!
問題文↓

数列Aと作成して欲しい数nがいくつか与えられるので、Aの中からいくつか取り出してnが作成できるかを確認してください

普通に全探索ですね!
制約がn≦20なので最大2²⁰でループできそうです
よってこれはO(2ⁿn)でクリアできます
以下のようになりました

#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (ll i = 0; i < (n); ++i)
#define all(a) (a).begin(), (a).end()
#define rall(a) (a).rbegin(), (a).rend()
#define yesNo(b) ((b) ? "yes" : "no")
using ll = long long;
using ull = unsigned long long;
string alphabet = "abcdefghijklmnopqrstuvwxyz";
string ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const double pi = 3.141592653589793;
int smallMOD = 998244353;
int bigMOD = 1000000007;

int main()
{
    // 入力.
    int n;
    cin >> n;
    vector<ll> A(n), B;
    rep(i, n) cin >> A[i];
    int m;
    cin >> m;

    // bit全探索.
    for (int i = 0; i < (1 << n); ++i)
    {
        ll x = 0;
        for (int j = 0; j < n; ++j)
        {
            if (i & (1 << j))
            {
                x += A[j];
            }
        }
        B.push_back(x);
    }

    // 存在するかの探索.
    rep(i, m)
    {
        int x;
        cin >> x;
        cout << yesNo(count(all(B), x) != 0) << endl;
    }
    return 0;
}

11 AtCoder Beginner Contest 128 C - Switches

問題はこちらです!
問題文↓

あるいくつかのスイッチが偶数個もしくは奇数個押されていたら光る電球がある
電球をすべて光らすためのスイッチのon/offは何通りあるか?

on/offっていうbit全探索っぽい感じです!
コードはこんな感じになりました

#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (ll i = 0; i < (n); ++i)
#define all(a) (a).begin(), (a).end()
#define rall(a) (a).rbegin(), (a).rend()
#define yesNo(b) ((b) ? "Yes" : "No")
using ll = long long;
using ull = unsigned long long;
string alphabet = "abcdefghijklmnopqrstuvwxyz";
string ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const double pi = 3.141592653589793;
int smallMOD = 998244353;
int bigMOD = 1000000007;

int main()
{
    int n, m;
    cin >> n >> m;
    vector<vector<int>> S(m);
    vector<int> P(m);
    rep(i, m)
    {
        int x;
        cin >> x;
        rep(j, x)
        {
            int y;
            cin >> y;
            S[i].push_back(y);
        }
    }
    rep(i, m) cin >> P[i];
    int ans = 0;
    for (int i = 0; i < pow(2, n); i++)
    {
        bitset<32> B(i);
        bool flag = true;
        rep(j, S.size())
        {
            int cnt = 0;
            rep(k, S[j].size())
            {
                if (B[S[j][k] - 1])
                    cnt++;
            }
            if (cnt % 2 != P[j])
            {
                flag = false;
            }
        }
        if (flag)
        {
            ans++;
        }
    }
    cout << ans << endl;
    return 0;
}

まだ、bit全探索の基本ですね

12 AtCoder Beginner Contest 002 D - 派閥

問題はこちらです!

友達リストが与えられる
できるだけ少ない(グループ内のすべての人が友達である)グループを作りたい
いくつのグループが出来るか

グラフでも解けそうだと感じましたが、bit全探索します
N個のbitを用意して、グループに入れていって友達かどうかを調べ上げたら良いですね!
コードはこうなりました

#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (ll i = 0; i < (n); ++i)
#define bitrep(i, n) for (int i = 0; i < (1 << n); ++i)
#define all(a) (a).begin(), (a).end()
#define rall(a) (a).rbegin(), (a).rend()
#define yesNo(b) ((b) ? "Yes" : "No")
using ll = long long;
using ull = unsigned long long;
string alphabet = "abcdefghijklmnopqrstuvwxyz";
string ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const double pi = 3.141592653589793;
int smallMOD = 998244353;
int bigMOD = 1000000007;

void recursive_comb(vector<int> indexes, int s, int rest, function<void(vector<int>)> f)
{
    if (rest == 0)
    {
        f(indexes);
    }
    else
    {
        if (s < 0)
            return;
        recursive_comb(indexes, s - 1, rest, f);
        indexes[rest - 1] = s;
        recursive_comb(indexes, s - 1, rest - 1, f);
    }
}

void foreach_comb(int n, int k, function<void(vector<int>)> f)
{
    vector<int> indexes(k);
    recursive_comb(indexes, n - 1, k, f);
}

vector<int> group;
vector<vector<bool>> A;
bool flag = true;
int main()
{
    int n, m;
    cin >> n >> m;
    int ans = 0;
    A.resize(n + 1, vector<bool>(n + 1, false)); // 人間関係の無向グラフを作成.
    rep(i, m)
    {
        // 互いに知り合いの状態.
        int a, b;
        cin >> a >> b;
        A[a - 1][b - 1] = true;
        A[b - 1][a - 1] = true;
    }

    // bit全探索で調べる.
    bitrep(i, n)
    {
        // groupの作成→bitが1の時groupに追加.
        group = {};
        bitset<12> bits(i);
        rep(j, 12)
        {
            if (bits[j])
            {
                group.push_back(j);
            }
        }

        flag = true;
        // groupの中に含まれる人が全員知り合いであるか.
        auto lambda = [](vector<int> indexes)
        {
            // Aのグループi番目が0なら友達でない.
            if (A[group[indexes[0]]][group[indexes[1]]] == 0)
            {
                flag = false;
                return;
            }
        };

        // Pythonで言うところのitertools.combinations().
        foreach_comb(group.size(), 2, lambda);

        // 全員友達だったら、ansの大きい方を取る.
        if (flag)
        {
            ans = max(ans, (int)group.size());
        }
    }
    cout << ans << endl;
    return 0;
}

コンビネーションを全列挙するのに、Pythonではitertools.combinationsを使えば良いのですが、C++では標準関数が無いので、誰かが作っていたものを盗みました
参照はこちらです!

13 JOI 2008 予選 5 - おせんべい

問題はこちらです!

おせんべい平面上にきれいに並べられている
これを行ごと、列ごとにひっくり返すことが出来る
最大でいくつのおせんべいを表向きにすることが出来るか

制約で、行は最大10までなので、行を最大2¹⁰回ループさせて固定してしまうことを考える!
すると、 O({2}^{H}HW) で実行することが出来ます!
こんな感じになりました!

#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (ll i = 0; i < (n); ++i)
#define bitrep(i, n) for (ll i = 0; i < (1 << n); ++i)
#define all(a) (a).begin(), (a).end()
#define rall(a) (a).rbegin(), (a).rend()
#define yesNo(b) ((b) ? "Yes" : "No")
using ll = long long;
using ull = unsigned long long;
string alphabet = "abcdefghijklmnopqrstuvwxyz";
string ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const double pi = 3.141592653589793;
int smallMOD = 998244353;
int bigMOD = 1000000007;

int main()
{
    int h, w;
    cin >> h >> w;
    vector<vector<int>> A(h, vector<int>(w)); // 白と黒の個数だけをメモしておく.
    rep(i, h)
    {
        rep(j, w)
        {
            cin >> A[i][j];
        }
    }
    // どの行を動かすかは固定する.
    vector<vector<int>> B(h, vector<int>(w));
    int ans = 0;
    bitrep(i, h)
    {
        // コピー.
        copy(all(A), B.begin());

        // 動かす行を反転させる.
        bitset<10> bits(i);
        rep(j, 10)
        {
            if (bits[j])
            {
                rep(k, w)
                {
                    B[j][k] = !B[j][k];
                }
            }
        }

        // 縦に見て0が多い方を取る.
        int x = 0;
        rep(j, w)
        {
            int zero = 0;
            rep(k, h)
            {
                if (B[k][j] == 0)
                    zero++;
            }
            x += max(zero, h - zero);
        }
        ans = max(ans, x);
    }
    cout << ans << endl;
    return 0;
}

14 Square869120Contest #4 B - Buildings are Colorful!

問題はこちらです!

N個の建物がある
左から見た時、K種類の建物が階段状に見えるようにしたい
ビルを1高くするのに、コストが1かかるので、最小でどれぐらいのコストがかかるか

こんな感じに考察しました!
・初めを除くどの建物を見えるようにするかをbit全探索.
・初め以外の建物で、K-1色のビルが見えれば良い.
・最大でも14C7=3432回の繰り返し.
ここでもまた、コンビネーション関数を使用しました

#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (ll i = 0; i < (n); ++i)
#define bitrep(i, n) for (ll i = 0; i < (1 << n); ++i)
#define all(a) (a).begin(), (a).end()
#define rall(a) (a).rbegin(), (a).rend()
#define yesNo(b) ((b) ? "Yes" : "No")
using ll = long long;
using ull = unsigned long long;
string alphabet = "abcdefghijklmnopqrstuvwxyz";
string ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const double pi = 3.141592653589793;
int smallMOD = 998244353;
int bigMOD = 1000000007;

void recursive_comb(vector<ll> indexes, ll s, ll rest, function<void(vector<ll>)> f)
{
    if (rest == 0)
    {
        f(indexes);
    }
    else
    {
        if (s < 0)
            return;
        recursive_comb(indexes, s - 1, rest, f);
        indexes[rest - 1] = s;
        recursive_comb(indexes, s - 1, rest - 1, f);
    }
}

void foreach_comb(ll n, ll k, function<void(vector<ll>)> f)
{
    vector<ll> indexes(k);
    recursive_comb(indexes, n - 1, k, f);
}

ll base;
vector<ll> A;
ll cost;
ll ans;
ll k, n;
int main()
{
    // どの建物を見えるようにするかをbit全探索.
    // 初め以外の建物で、K-1色のビルが見えれば良い.
    // 最大でも14C7=3432回の繰り返し.
    cin >> n >> k;
    A.resize(n);
    rep(i, n) cin >> A[i];
    ans = INT64_MAX;
    auto lambda = [](vector<ll> indexes)
    {
        base = A[0];
        cost = 0;
        int j = 0;
        rep(i, n - 1)
        {
            if (i + 1 == indexes[j] + 1)
            {
                // 0以下の場合基準を変えるだけでコストは掛からない.
                if (base - A[indexes[j] + 1] + 1 < 0)
                {
                    base = A[indexes[j] + 1];
                }
                else
                {
                    cost += base - A[indexes[j] + 1] + 1;
                    base = base + 1;
                }
                j++;
            }
            else
            {
                // もし、選ばれていないindexなら、基準が変わる可能性がある.
                // 3 5 2.なら途中の基準は5になる.
                base = max(base, A[i + 1]);
            }
        }
        ans = min(ans, cost);
    };
    // 初めはA[0]を基準にどれをどれだけ高くするか.
    foreach_comb(n - 1, k - 1, lambda);
    cout << ans << endl;
    return 0;
}

なんでC++の標準にコンビネーション関数無いんだろ、、、?

終わりに...

お疲れ様でした! ではまた!