【二進位制列舉】2022牛客寒假集訓5 C-戰旗小孩
阿新 • • 發佈:2022-02-13
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; }