srm 549

250


Description

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

  • (1): Ha / Ra > Hb / Rb
  • (2): Ra < Rb

Solution

很显然的一个二分图匹配

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#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\times N(N\le 13)$的矩阵上,有一些$H$格子, 一开始每个$H$格子里最多有一枚带权值的硬币。 每次小$B$选一个$H$格子,然后小$A$将硬币任意排列后,将该格子的硬币给小$B$。
排列后必须满足如下条件:

  • (1): 每个格子必须有最多一枚硬币,小$B$选的格子可以没有硬币
  • (2): 每一行,每一列,$H$的个数和硬币的个数之和必须是偶数

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

Solution

观察到,如果$B$能获得$k$个硬币的话,一定获得的是权值最小的$k$的硬币。
考虑到$H$格子的情况,其实只有三种

  • (1):没选择这个格子
  • (2): 选择了这个格子,下面没有硬币
  • (3): 选择了这个格子,下面有硬币

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

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#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;
}
};