CF321B 狐狸打牌
1 CF321B Ceil 和 Jiro
2 题目描述
时间限制 \(2s\) | 空间限制 \(256M\)
狐狸 \(Ciel\) 和他的朋友 \(Jiro\) 玩一个纸牌游戏,\(Jiro\) 有 \(n\) 张纸牌,每张纸牌都有两个属性:功能(攻击或者防御)和能量。\(Ciel\) 有 \(m\) 张纸牌,每张纸牌也有两个相同的属性。已知 \(Ciel\) 所有纸牌的功能都是攻击。现在轮到 \(Ciel\) 出牌,他可以进行很多次下面的操作:
- 选择一张她的卡片 \(X\)。这张卡片必须从未被选中过。
- 如果 \(Jiro\) 这时没有激活的卡片,那么他收到 \(X\) 点伤害。否则 \(Ciel\) 需要选择一张 \(Jiro\) 的激活卡片 \(Y\),接着:
- 如果 \(Y\) 的功能是攻击,那么如果 \(X\) 的能量大于等于 \(Y\) 的能量,在这次攻击后卡片 \(Y\) 死亡,\(Jiro\) 收到 \(X-Y\) 点攻击。
- 如果 \(Y\) 的功能是防守,那么如果 \(X\) 的能量大于 \(Y\) 的能量,在这次攻击后,卡片 \(Y\) 死亡,但是 \(Jiro\) 没有收到伤害。
\(Jiro\) 任何时候都可以停止战斗状态(所以她的卡片不需要一定用完)。计算 \(Jiro\) 能受到的最大伤害值总和。
数据范围:\(1 ≤ n, m ≤ 100\), \(0 ≤ strength ≤ 8000\)。
3 题解
我们分类讨论:最优策略肯定是将敌人的所有卡消灭后打出真实伤害或者最大化我方的卡打对方的 \(ATK\) 卡所造成的伤害这两者之一。这是因为,我们无法又最大化伤害又去将敌人的所有卡消灭。如果数据可以保证我们又最大化对 \(ATK\) 的伤害又将所有敌人的卡消灭,那这种方案不优于最小化消灭对方所有卡的代价,然后打真实伤害造成的伤害,因为如果我们可以消灭所有敌人的卡,那么我们所浪费在 \(ATK\) 防御上的伤害是相同的,而由于我们最大化了 \(ATK\) 的伤害,我们会浪费一些攻击在无法造成伤害的 \(DEF\) 卡上。而我们策略一中先考虑的就是无法造成伤害的 \(DEF\) 卡,选择攻击与其最接近的卡,所以一定不会更劣。
具体地,我们在第一个策略中先找所有的 \(DEF\) 卡,然后找到离每张 \(DEF\) 卡点数最近且大于该点数的攻击卡,利用该卡将 \(DEF\) 卡消灭。如果我们在中途发现无法消灭某张 \(DEF\) 卡,那么我们第一个策略就不成立。否则,我们继续消灭 \(ATK\) 卡。由于这里 \(ATK\) 卡可以抵消掉的攻击是固定的,所以我们的顺序没有任何关系。在第二种策略中,我们将所有的 \(ATK\) 卡按点数递增排序,将我们的攻击递减排序。这样一来,我们能最大化我们比当前 \(ATK\) 卡点数大的可能性,且我们可以最小化被抵消的攻击。最终只需要取这两个值的最大值即可。
4 代码(空格警告):
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 105;
int n, m, v, ans, minn, pos, ans1, cnt;
string s;
struct card
{
int type, val;
}a[N];
int b[N];
bool f[N], flag;
bool cmp1(int x, int y)
{
return x > y;
}
bool cmp2(card x, card y)
{
return x.val < y.val;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
cin >> s >> v;
a[i].val = v;
if (s == "ATK") a[i].type = 1;
else a[i].type = 2;
}
for (int i = 1; i <= m; i++) cin >> b[i];
//try to kill all the cards
for (int i = 1; i <= n; i++)
{
if (a[i].type == 1) continue;
minn = 0x3f3f3f3f;
pos = 0;
for (int j = 1; j <= m; j++)
{
if (f[j]) continue;
if (b[j] > a[i].val && minn > b[j] - a[i].val)
{
minn = b[j] - a[i].val;
pos = j;
}
}
if (!pos)
{
flag = 1;
break;
}
f[pos] = 1;
}
for (int i = 1; i <= n; i++)
{
if (a[i].type == 2) continue;
minn = 0x3f3f3f3f;
pos = 0;
for (int j = 1; j <= m; j++)
{
if (f[j]) continue;
if (b[j] >= a[i].val && minn > b[j] - a[i].val)
{
minn = b[j] - a[i].val;
pos = j;
}
}
if (!pos || flag)
{
flag = 1;
break;
}
f[pos] = 1;
ans1 += minn;
}
for (int i = 1; i <= m; i++) if (!f[i] && !flag) ans1 += b[i];
ans = ans1;
ans1 = 0;
sort(b+1, b+m+1, cmp1);
sort(a+1, a+n+1, cmp2);
for (int i = 1; i <= n; i++)
{
if (a[i].type == 2) continue;
cnt++;
if (cnt > m) break;
if (b[cnt] >= a[i].val) ans1 += b[cnt] - a[i].val;
}
ans = max(ans, ans1);
cout << ans;
return 0;
}