【二进制枚举】2022牛客寒假集训5 C-战旗小孩


C - 战棋小孩

题目来源:2022牛客寒假算法基础集训营5

题目链接:C-战棋小孩_2022牛客寒假算法基础集训营5 (nowcoder.com)

题目

酒馆战棋排行榜记录了全国积分最高的200个账号,上榜是九峰的梦想。
这一天九峰进行了nnn局游戏,每局游戏都可以从两个英雄中选择一位,由于九峰的发挥过于稳定,所以他的游戏结果只与选择英雄的强度相关,并且九峰手中有若干次礼遇的机会,使用后能使得本局游戏能额外多出两个英雄可以选择,但礼遇只能在选择前使用,也就是说九峰不能在看见两个可选英雄后再考虑是否使用礼遇。
每局游戏后九峰都会看一眼排行榜,如果他上榜了就会很开心。
九峰想上榜的欲望过于强烈以至于打动了时空之神,神给与了他自由排列游戏顺序的能力
现在给出每局游戏的选将情况和结束后上榜需要的分数,和每局游戏结束后,排行榜上需要的分数,九峰想知道通过能力合理排列并进行最优选择后,他最多因上榜而开心的次数。

image-20220213180125012

image-20220213180140845

思想

主要算法:贪心 + 二进制枚举

首先不难发现,要想上榜次数最多,肯定是加的分越快越好(快的意思是,优先进行分数最高的场)。

但因为使用礼遇的局次无法确定(总之就是很复杂),所以使用二进制将每一种情况枚举出来,选出上榜次数最多的一种情况就好了!

代码

#include 
#include 
#include 
#include 
#include 
#include 
#define SF ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
using namespace std;
typedef long long ll;
typedef pair 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 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;
}