题解 For Problem. 就你开挂是吧
题目描述
CSGO,原名 Counter-Strike: Global Offensive ,是一款由VALVE与Hidden Path Entertainment合作开发、Valve Software发行的第一人称射击游戏,是一款目前已经风靡全球的竞技类游戏。
最近国内举行了 CSGO单挑锦标赛,比赛的赛制是:每一场比赛一共会比 n 小局,每赢一小局比赛可以获得 1 或 2 分:若前一场也胜利, 则可获得2分,若前一场失败,则只获得1分。失败不得分。如果你在比赛的第一局中获胜,你将获得1分(因为没有“前一局”)。当n局比赛全部结束的时候,分数更高的一方即可获得胜利。
小周学长也特别喜欢玩CSGO,于是非常踊跃地参加了比赛。然而,参加比赛之后,小周学长发现自己比较菜,在预选赛的时候就已经非常吃力了。就在他一筹莫展之时,某夕夕上的一款商品吸引了他的注意: 《 CSGO辅助科技(simple用了都说好)》。小周学长高兴地付了款,拿到了插件。使用过后,小周学长发现只要在一局比赛里插件生效,就可以赢下这局比赛。不过经过测试,小周学长却发现了一个问题: 这个插件并不是每一局都会生效! 也就是说,如果这一小局比赛之中,插件并未生效,小周学长将 输掉这一小局比赛。
幸好,小周学长经过研究,发现插件是否生效是有规律的: 每一场比赛中,这个插件只能生效 k 次。利用这一规律,小周学长成功打入了决赛。总决赛的对手是琪琪学姐,不过小周学长并不想因为这样就放水;相反,小周学长想在总决赛中 拿下尽可能多的分数。现在小周学长通过超强的大脑预测出了如果不使用插件的比赛的每一小局的结果,请你帮他计算以最佳方式开挂可以获得的 最高分数。
n 局比赛的结果由长度为 n 的字符串 s 表示:
-
如果您赢了第 i 场比赛,s 的第 i 个字符为 W;
-
如果您输了第 i 次比赛,则为 L。
解决方案
首先,我们可以注意到关于得分,有: {score} = 2 * {wins} - {linkwins}
其中连胜(linkwins)是连续获胜的最大序列。在下面的解释中,变量 wins、linkwins 始终与初始情况相关。
如果 k + wins ≥ n , 那么就有可能赢得所有比赛,因此答案是 2n−1 。
否则,很明显,我们希望将k次损失转化为k次胜利。因此,在作弊之后,赢的次数将是 k + wins 。考虑到上面的公式,它仍然只是为了最小化获胜的连胜次数。
我们如何将连胜次数降至最低?很直观的是,我们将从最短的间隔开始,按长度的递增顺序,填充连续获胜条纹之间的间隔。这可以证明,如果 gap 差距没有被填补(即在开挂后,每个 gap 差距仍然至少包含一次损失),那么至少有 gap+1 次连胜。
实现如下:通过线性扫描,我们找到间隙的长度,然后对其进行排序。最后,我们计算最多可以填充的间隔数 filledgaps 即可。
那么答案就是 2 * (k + {wins}) - {linkwins} + {filledgaps} 。
解决方案的复杂性为 O(nlog(n)) 。
AC代码
c++
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int T;
cin >> T;
for (int t = 1; t <= T; t++)
{
int N, K;
cin >> N >> K;
string S;
cin >> S;
int winning_streaks_cnt = 0;
int wins = 0;
int losses = 0;
vector<int> losing_streaks;
for (int i = 0; i < N; i++)
{
if (S[i] == 'W')
{
wins++;
if (i == 0 or S[i - 1] == 'L')
winning_streaks_cnt++;
}
if (S[i] == 'L')
{
losses++;
if (i == 0 or S[i - 1] == 'W')
losing_streaks.push_back(0);
losing_streaks.back()++;
}
}
if (K >= losses)
{
cout << 2 * N - 1 << "\n";
continue;
}
if (wins == 0)
{
if (K == 0) cout << 0 << "\n";
else cout << 2 * K - 1 << "\n";
continue;
}
if (S[0] == 'L')
losing_streaks[0] = 1e8;
if (S[N - 1] == 'L')
losing_streaks.back() = 1e8;
sort(losing_streaks.begin(), losing_streaks.end());
wins += K;
for (int ls : losing_streaks)
{
if (ls > K) break;
K -= ls;
winning_streaks_cnt--;
}
cout << 2 * wins - winning_streaks_cnt << "\n";
}
return 0;
}
python
n = int(input())
for _ in range(n):
num = list(map(int, input().split()))
s = input()
winning_streaks_cnt = 0
wins = 0
losses = 0
losing_streaks = []
for i in range(num[0]):
if s[i] == 'W':
wins += 1
if i == 0 or s[i - 1] == 'L':
winning_streaks_cnt += 1
if s[i] == 'L':
losses += 1
if i == 0 or s[i - 1] == 'W':
losing_streaks.append(0)
losing_streaks[-1] += 1
if num[1] >= losses:
print(2 * num[0] - 1)
continue
if wins == 0:
if num[1] == 0:
print(0)
else:
print(2 * num[1] - 1)
continue
if s[0] == 'L':
losing_streaks[0] = 10000000000
if s[num[0] - 1] == 'L':
losing_streaks[-1] = 10000000000
losing_streaks.sort()
wins += num[1]
for ls in losing_streaks:
if ls > num[1]:
break
num[1] -= ls
winning_streaks_cnt -= 1
print(2 * wins - winning_streaks_cnt)