srm 549

欢迎点此阅读QvQ

250


Description

有两个集合,每个集合有 N(N<50) 个锥面(表示为高 H 和底面半径R,均小于 10000 )。要求从集合 1 和集合2的笛卡尔积集合中选取最多的元素满足如下性质:
* (1): Ha / Ra > Hb / Rb
* (2): Ra < Rb

Solution

很显然的一个二分图匹配

Code

#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define mp make_pair
#define F first
#define S second
typedef long long LL;
typedef pair<int, int> pii;
const int N = 55;
int g[N][N], l[N], vis[N];
bool can(int u, int m) {
    for (int i = 0; i < m; ++i) {
        if (g[u][i] && !vis[i]) {
            vis[i] = 1;
            if (l[i] == -1 || can(l[i], m)) {
                l[i] = u;
                return 1;
            }
        }
    }
    return 0;
}
struct PointyWizardHats {
    int getNumHats(vector <int> th, vector <int> tr, vector <int> bh, vector <int> br) {
        int n = th.size(), m = bh.size();
        memset(l, -1, sizeof(l));
        for (int i = 0; i < n; ++i)
            for (int j = 0; j < m; ++j) {
                if (th[i] * br[j] > tr[i] * bh[j] && tr[i] < br[j]) g[i][j] = 1;
            }
        int ans = 0;
        for (int i = 0; i < n; ++i) {
            memset(vis, 0, sizeof(vis));
            if (can(i, m))  ++ans;
        }
        return ans;
    }
};

600


Description

A B博弈。 在 N×N(N13) 的矩阵上,有一些 H 格子, 一开始每个H格子里最多有一枚带权值的硬币。 每次小 B 选一个H格子,然后小 A 将硬币任意排列后,将该格子的硬币给小B
排列后必须满足如下条件:
* (1): 每个格子必须有最多一枚硬币,小 B 选的格子可以没有硬币
* (2): 每一行,每一列,H的个数和硬币的个数之和必须是偶数

B 想获得尽量多的权值,A想让对面获得尽量少的权值

Solution

观察到,如果 B 能获得k个硬币的话,一定获得的是权值最小的 k 的硬币。
考虑到H格子的情况,其实只有三种
* (1):没选择这个格子
* (2): 选择了这个格子,下面没有硬币
* (3): 选择了这个格子,下面有硬币

容易想到三进制压缩,考虑状压dp,用三进制状态表示,用记忆化搜索比较容易实现。枚举当前选择的格子,枚举两种有无硬币的情况即可。

Code

#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define mp make_pair
#define F first
#define S second
typedef long long LL;
typedef pair<int, int> pii;
vector<pii> g;
const int N = 13, inf = 100;
struct MagicalHats {
    int p[N], r[N], c[N], dp[1594323];
    int n, m, totcoin, tothat, num;
    inline int gao(int mask, int i, int s) {// 0:not selected 1:no coin 2:has coin
        return mask + s * p[i];
    }
    inline int get(int mask, int i) {
        return (mask / p[i]) % 3;
    }
    int dfs(int mask, int cocnt, int hatcnt, int r[], int c[]) {
        if (~dp[mask]) return dp[mask];
        int &t = dp[mask];
        t = -inf;
        if (hatcnt == tothat) {
            if (cocnt != totcoin)   return t = -inf;
            for (int i = 0; i < n; ++i) 
                if (r[i] & 1)   return t = -inf;
            for (int i = 0; i < m; ++i)
                if (c[i] & 1)   return t = -inf;
            return t = 0;
        }
        for (int i = 0; i < tothat; ++i) {
            int x = g[i].F, y = g[i].S;
            int s = get(mask, i);
            if (!s) {//not selected
                int t1 = -inf, t2 = -inf, state;
                if (cocnt < totcoin) {//has coin
                    int cnt = 1;
                    if (hatcnt >= num)  cnt = 0;
                    state = gao(mask, i, 2);
                    r[x] += 2;
                    c[y] += 2;
                    t1 = dfs(state, cocnt + 1, hatcnt + 1, r, c) + cnt;
                    r[x] -= 2;
                    c[y] -= 2;
                }
                //no coin
                ++r[x];
                ++c[y];
                state = gao(mask, i, 1);
                t2 = dfs(state, cocnt, hatcnt + 1, r, c);
                --r[x];
                --c[y];
                if (t1 < 0) t = max(t, t2);
                if (t2 < 0) t = max(t, t1);
                t = max(t, min(t1, t2));
            }
        }
        return t;
    }
    int findMaximumReward(vector <string> board, vector <int> coins, int numGuesses) {
        p[0] = 1;
        for (int i = 1; i < 13; ++i)    p[i] = p[i - 1] * 3;
        this -> n = board.size(), this -> m = board[0].size();
        for (int i = 0; i < n; ++i)
            for (int j = 0; j < m; ++j)
                if (board[i][j] == 'H') g.pb(mp(i, j));
        this -> totcoin = coins.size(), this -> tothat = g.size(), this -> num = numGuesses;
        memset(dp, -1, sizeof(dp));
        int cnt = dfs(0, 0, 0, r, c);
        sort(coins.begin(), coins.end());
        if (cnt < 0)    return -1;
        int ans = 0;
        for (int i = 0; i < cnt; ++i)   ans += coins[i];
        return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值