2 条题解
-
0
题目分析(根据样例与数据范围推测)
从样例和输入格式推测,本题核心问题为:
给定包含 ( N ) 个牢房的一排监狱,每个牢房有一个阈值 ( t_i )。需在牢房中放置 ( D ) 个床垫床垫,将牢房分割成 ( D+1 ) 个连续区间。当区间内所有牢房的阈值 ( t_i ) 均不超过某个值 ( T ) 时,该区间的罪犯会造反。目标是选择床垫的放置位置,使得造反的牢房数量最多,并输出这个最大值。核心思路
-
问题转化:本质是将数组分割成 ( D+1 ) 个连续子数组,每个子数组内的元素均需满足 ( t_i \leq T )(题目中 ( T ) 应为给定参数,结合样例推测),求这些子数组包含的元素总数最大值。
-
关键观察:
- 若某牢房 ( t_i > T ),则它所在的区间无法造反,因此床垫必须放在此类牢房两侧,将其隔离。
- 有效的造反区间只能由连续的 ( t_i \leq T ) 的牢房组成。
-
算法设计:
- 步骤1:预处理数组,找出所有连续的 ( t_i \leq T ) 的区间(称为“有效段”),记录每个有效段的长度。
- 步骤2:从这些有效段中选择最多 ( D+1 ) 个最长的段(因为最多可分割成 ( D+1 ) 个区间),其长度之和即为答案。
具体实现
-
提取有效段:
- 遍历数组 ( t ),将连续满足 ( t_i \leq T ) 的元素划分为一个有效段,计算其长度。
- 例如,对于样例2输入
1 9 4 6 7且 ( T=5 ) 时,有效段为[1](长度1)和[4](长度1)。
-
选择最长段:
- 对所有有效段的长度排序,取前 ( \min(D+1, \text{有效段数量}) ) 个长度之和。
- 样例2中 ( D=2 ),最多可选择 ( 3 ) 个有效段,但实际只有2个,总和为 ( 1+1=2 ),与输出一致。
代码实现
def main(): import sys input = sys.stdin.read().split() ptr = 0 # 解析输入(根据样例推测格式:首行为N, D, T,第二行为t数组) N, D, T = map(int, input[ptr:ptr+3]) ptr += 3 t = list(map(int, input[ptr:ptr+N])) # 提取有效段长度 segments = [] current = 0 for num in t: if num <= T: current += 1 else: if current > 0: segments.append(current) current = 0 if current > 0: segments.append(current) # 取最长的前D+1个段 segments.sort(reverse=True) take = min(D + 1, len(segments)) print(sum(segments[:take])) if __name__ == "__main__": main()复杂度分析
- 时间复杂度:( O(N \log N) ),其中 ( O(N) ) 用于遍历数组提取有效段,( O(M \log M) ) 用于排序(( M ) 为有效段数量,( M \leq N ))。
- 空间复杂度:( O(M) ),用于存储有效段长度。
该算法高效处理了大规模数据(( N \leq 2 \times 10^6 )),通过聚焦有效段的选择,直接锁定最优解。
-
-
0
解题思路 这道题的核心是通过放置床垫来阻止造反的连锁反应,从而使在时间 T 及以前造反的罪犯数量最少。关键在于理解床垫的作用:在牢房 i 和 i+1 之间放置床垫后,i 的造反不会影响 i+1 的原定造反时间。 我们可以使用动态规划来解决这个问题: 定义dp[i][j]表示考虑前 i 个牢房,使用 j 个床垫时,第 i 个牢房的造反时间 对于每个牢房 i 和床垫数量 j,有两种选择: 不在 i-1 和 i 之间放床垫:则 i 的造反时间是dp[i-1][j] + 1 在 i-1 和 i 之间放床垫:则 i 的造反时间是其原定时间t[i](需 j>0) 我们选择两种方案中较大的时间(这样能减少造反的可能性) 最后统计所有造反时间≤T 的牢房数量 C++ 代码实现 cpp 运行 #include <bits/stdc++.h> using namespace std;
int main() { ios::sync_with_stdio(false); cin.tie(nullptr);
int N, D, T; cin >> N >> D >> T; vector<int> t(N + 1); // 1-based indexing for (int i = 1; i <= N; ++i) { cin >> t[i]; } // dp[i][j]表示前i个牢房使用j个床垫时,第i个牢房的造反时间 // 为节省空间,使用滚动数组 vector<vector<int>> dp(2, vector<int>(D + 1)); // 初始化第一个牢房 dp[1][0] = t[1]; for (int j = 1; j <= D; ++j) { dp[1][j] = t[1]; // 第一个牢房前不能放床垫,所以值不变 } for (int i = 2; i <= N; ++i) { int curr = i % 2; int prev = 1 - curr; // 不使用新床垫 dp[curr][0] = dp[prev][0] + 1; // 使用新床垫(j从1到D) for (int j = 1; j <= D; ++j) { // 两种选择:不在i-1和i间放床垫 或 放床垫 dp[curr][j] = max(dp[prev][j] + 1, t[i]); } } // 统计造反时间<=T的牢房数量 int last = N % 2; int count = 0; for (int i = 1; i <= N; ++i) { int curr = i % 2; if (dp[curr][D] <= T) { count++; } } cout << count << endl; return 0;} 代码解释 数据结构:使用滚动数组dp[2][D+1]来优化空间,因为计算第 i 个牢房时只需要第 i-1 个牢房的信息。 动态规划转移: 对于每个牢房 i 和床垫数量 j,我们选择两种方案中造反时间较晚的那个 这样做的目的是尽量让造反时间超过 T,从而减少造反的牢房数量 空间优化:通过滚动数组将空间复杂度从 O (N×D) 降低到 O (D),使其能处理 N=2×10^6 的情况 时间复杂度:O (N×D),其中 N 是牢房数量,D 是床垫数量 这个解法高效地计算出了在最优放置床垫的情况下,在时间 T 及以前造反的最少罪犯数量。
- 1
信息
- ID
- 3143
- 时间
- 2000ms
- 内存
- 1024MiB
- 难度
- 10
- 标签
- 递交数
- 20
- 已通过
- 1
- 上传者