dp1
题目: Cake Trial
题目背景
GLaDOS 为 Chell 设计了一项测试。有 n 个蛋糕排成一行,每个蛋糕可能是真的(T)或假的(F)。Chell 可以准确判断每个蛋糕的真假,但她的回答必须满足:所有她认为是假的蛋糕必须形成一个连续的子段(可以为空)。
题目描述
初始时,GLaDOS 已经放置了一些蛋糕,但有些位置尚未确定,用字符 N 表示。GLaDOS 可以自由地将每个 N 替换为 T 或 F,以最大化 Chell 被迫犯的错误数。
Chell 在看到最终排列后,会选择一段连续区间作为她声称的假蛋糕集合(区间外都视为真),并最小化错误数。
请你计算 GLaDOS 能保证的最大错误数。
输入格式
- 第一行:一个整数
t(1 ≤ t ≤ 10⁴),表示测试用例数。 - 每个测试用例:
- 第一行:一个整数
n(1 ≤ n ≤ 500),表示蛋糕数量。 - 第二行:一个长度为
n的字符串s,由字符T、F、N组成。
- 第一行:一个整数
数据保证:所有测试用例的 n³ 之和不超过 500³。
输出格式
对于每个测试用例,输出一个整数 —— GLaDOS 能保证的最大错误数。
核心思路(简要)
- 若 Chell 选择区间
[l, r],则错误数
M = F_total - (F_in - T_in),其中F_total为总假蛋糕数,F_in和T_in为区间内假/真蛋糕数。 - 将
F看作+1,T看作-1,则F_in - T_in就是区间和。Chell 要最小化M,等价于最大化区间和,即最大子段和Smax(允许空区间,故Smax ≥ 0)。 - 因此,对于固定排列,Chell 的最小错误数为
F_total - Smax。 - GLaDOS 要最大化该值,即填充所有
N后使F_total - Smax最大。 - 采用三维 DP:
- 状态
dp[i][f][s]:处理完前i个位置,总假数为f,以i结尾的最大子段和为s时,全局最大子段和Smax的最小可能值。 - 转移:根据当前位置放
F或T更新f、s及Smax。 - 最终答案:
max_{f, s} ( f - dp[n][f][s] )。
- 状态
- 时间复杂度
O(n³),满足题目限制。 - 题目链接:https://codeforces.com/contest/2238/problem/E
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int INF = 1e9;
void solve() {
int n; string s; cin >> n >> s;
vector<vector<int>> dp(n + 1, vector<int>(n + 1, INF));
dp[0][0] = 0;
for(int i = 0; i < n; i ++) {
vector<vector<int>> ndp(n + 1, vector<int>(n + 1, INF));
char ch = s[i];
for(int f = 0; f <= i; f ++) {
for(int cur = 0; cur <= i; cur ++) {
if(dp[f][cur] == INF) continue;
if(ch == 'F' || ch == 'N') {
int nf = f + 1;
int ncur = cur + 1;
int maxv = max(dp[f][cur], ncur);
ndp[nf][ncur] = min(ndp[nf][ncur], maxv);
}
if(ch == 'T' || ch == 'N') {
int nf = f;
int ncur = max(0, cur - 1);
int maxv = dp[f][cur];
ndp[nf][ncur] = min(ndp[nf][ncur], maxv);
}
}
}
dp.swap(ndp);
}
int ans = 0;
for(int f = 0; f <= n; f ++) {
for(int cur = 0; cur <= n; cur ++) {
if(dp[f][cur] != INF) ans = max(ans, f - dp[f][cur]);
}
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false); cin.tie(0);
int t; cin >> t;
while(t --) solve();
return 0;
}

浙公网安备 33010602011771号