1. 程式人生 > 其它 >【二進位制列舉】2022牛客寒假集訓5 C-戰旗小孩

【二進位制列舉】2022牛客寒假集訓5 C-戰旗小孩

C - 戰棋小孩

題目來源:2022牛客寒假演算法基礎集訓營5

題目連結:C-戰棋小孩_2022牛客寒假演算法基礎集訓營5 (nowcoder.com)

題目

酒館戰棋排行榜記錄了全國積分最高的200個賬號,上榜是九峰的夢想。
這一天九峰進行了nnn局遊戲,每局遊戲都可以從兩個英雄中選擇一位,由於九峰的發揮過於穩定,所以他的遊戲結果只與選擇英雄的強度相關,並且九峰手中有若干次禮遇的機會,使用後能使得本局遊戲能額外多出兩個英雄可以選擇,但禮遇只能在選擇前使用,也就是說九峰不能在看見兩個可選英雄後再考慮是否使用禮遇。
每局遊戲後九峰都會看一眼排行榜,如果他上榜了就會很開心。
九峰想上榜的慾望過於強烈以至於打動了時空之神,神給與了他自由排列遊戲順序的能力
現在給出每局遊戲的選將情況和結束後上榜需要的分數,和每局遊戲結束後,排行榜上需要的分數,九峰想知道通過能力合理排列並進行最優選擇後,他最多因上榜而開心的次數。

思想

主要演算法:貪心 + 二進位制列舉

首先不難發現,要想上榜次數最多,肯定是加的分越快越好(快的意思是,優先進行分數最高的場)。

但因為使用禮遇的局次無法確定(總之就是很複雜),所以使用二進位制將每一種情況枚舉出來,選出上榜次數最多的一種情況就好了!

程式碼

#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <queue>
#include <vector>
#define SF ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
using namespace std;
typedef long long ll;
typedef pair<ll, ll> P;
const int inf = 0x3f3f3f3f;
int n, k, s;
int p[50], v[50], w[50], fin[50];
int popcount(ll x) {
    int cnt = 0;
    while (x) {
        if (x & 1) cnt++;
        x >>= 1;
    }
    return cnt;
}
int cal(int x) {
    for (int i = 0; i < n; ++i) {
        if (x >> i & 1)
            fin[i + 1] = w[i + 1];
        else
            fin[i + 1] = v[i + 1];
    }
    sort(fin + 1, fin + 1 + n, greater<int>());
    int res = s, cnt = 0;
    for (int i = 1; i <= n; ++i) {
        res += fin[i];
        if (res >= p[i]) cnt++;
    }
    return cnt;
}
int main() {
    SF;
    cin >> n >> k >> s;
    for (int i = 1; i <= n; ++i) cin >> p[i];
    for (int i = 1, a, b, c, d; i <= n; ++i) {
        cin >> a >> b >> c >> d;
        v[i] = max(a, b);
        w[i] = max(v[i], max(c, d));
    }
    int mx = 1 << n, ans = -inf;
    for (int i = 0; i < mx; ++i) {
        if (popcount(i) == k) ans = max(ans, cal(i));
    }
    cout << ans << endl;
    return 0;
}