2024年少年宫高级C1班
已结束
IOI
开始于: 2024-6-16 15:45
400
小时
主持人:
15
考试时间:16:00 - 17:30
讲解:http://www.temege.com/file/1124/2024年6月测试题目讲解.pptx
- 参考代码: -T1:
-T2:
# include <iostream>
using namespace std;
const int N = 2e3 + 5;
int f[N][N]; //从a[1- i], 到b[1 j]的编辑距离
// 相等
// f[i][j] = f[i-1][j-1]
// 修改
// f[i][j] = f[i-1][j-1] + 1
// 插入
// f[i][j] = f[i][j-1] + 1
// 删除
// f[i][j] = f[i-1][j] + 1
int main() {
string a, b;
cin >> a;
cin >> b;
for (int i = 1; i <= a.length(); i ++)
f[i][0] = i;
for (int j = 1; j <= b.length(); j ++)
f[0][j] = j;
for (int i = 1; i <= a.length(); i ++ ) {
for (int j = 1; j <= b.length(); j ++) {
if (a[i - 1] == b[j - 1])
f[i][j] = f[i - 1][j - 1];
else {
f[i][j] = min(f[i - 1][j - 1], min(f[i][j - 1], f[i - 1][j])) + 1;
}
}
}
cout << f[a.length()][b.length()];
return 0;
}
-T3:
//参考代码:
/*********************************************************************
程序名: <FILENAME>
作者: Geek+
日期: 2024/6/14 10:05:07
说明:
*********************************************************************/
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <stack>
#include <queue>
/*
解题思路:
朴素方法:
假设 f[i] 表示到达第 i 个位置最少踩到石子数
如果要到达第 i 个位置, 只能从 [i - S, i - T]
所以动态转移方程为 f[i] = min(f[j]) + flag[i]; (j >= i - S, j <= i - T) flag[i] 表示 第i个位置是否有石子
优化方法1:压缩区间
①观察数据范围,L较大,M比较小,说明石子都是稀疏的摆在区间[0, L] 之上
②S和T比较小, 所以可以压缩区间,
假设最大走2,最小走1, 那么可以跳跃的点为: 0 1 2 3 4 5 6,都是连续的
假设最大走4,最小走3, 那么可以跳跃的点为: 0 3 4 6 7 8 9 ,6之后都是连续的
假设最大走5,最小走4, 那么可以跳跃的点为: 0 4 5 8 9 10 12 13 14 15 ,12之后都是连续的
假设最大走10,最小走9, 那么可以跳跃的点为:
0 9 10 18 19 20 27 28 29 30 36 37 38 39 40 45 46 47 48 49 50
54 55 56 57 58 59 60 63 64 65 66 67 68 69 70 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 ,81之后都是连续的
最坏情况:最大走10,最小走9,意味着81以后都是连续的点
那么在计算 f[i] = min(f[j]) + flag[i]; 的时候, 可以把两个石子的大区间缩小至 81 以内就行,为了好计算 缩小至100
重新处理石子之间的距离,由于石子位置不是有序的,可以先排序,然后累加每一位置偏移量,进行偏移
优化方法2: 单调队列
*/
using namespace std;
const int N = 1e6+10;
// 由于桥太长,可以压缩长度
int f[N]; // f[i] 表示到达第i个位置最少踩到石子的石子数
int flag[N]; //标识第i个位置是否有石子
int arr[N];
int L;
int S, T , M;
int solve(int n) {
int offset = 0;
// 左向偏移,假如第一个石子到第二个石子距离为500,压缩到100就行了,那就意味之后所有石子都要向右偏移400
int last = 0;
for(int i = 1; i <= n; i ++) {
if(arr[i] - last >= 100) {
offset = (offset + arr[i] - last - 100); // 计算偏移量
}
last = arr[i];
arr[i] -= offset;
flag[arr[i]] = true;
}
for(int i = 0; i <= arr[n]; i ++) {
f[i] = 101;
}
int ans = 0;
for(int i = S; i <= arr[n]; i ++) {
for(int j = S; j <= T; j ++) {
f[i]= min(f[i], f[i-j] + flag[i]);
}
//ans = max(ans, f[i]);
}
for(int i = 1; i <= T; i ++) {
ans = max(ans, f[arr[n] - i]);
}
return ans;
}
int main() {
cin >> L;
cin >> S >> T >> M;
for(int i = 1; i <= M; i ++) {
cin >> arr[i];
}
if(S == T) { // 特判
int ans = 0;
for(int i = 1; i <= M; i ++){
if(arr[i]%S == 0){
ans += 1;
}
}
cout << ans << endl;
} else {
sort(arr+1, arr + M + 1);
f[0] = 0;
arr[M+1] = L;
cout << solve(M+1) << endl;
}
return 0;
}
-T4:
//参考代码:
/*********************************************************************
程序名: <FILENAME>
作者: Geek+
日期: 2024/6/17 12:43:34
说明:
*********************************************************************/
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <stack>
#include <queue>
/*
解题思路:
朴素方法:
f[i] = min f[j] + (cnt[i] - cnt[j]) * i - (sum[i] - sum[j])); j 在区间[0, i - m] 如果你能理解过程 j范围缩小至 [i - 2 * m, i - m]
参考过河那道题
优化方法1: 观察数据集:时间跨度较大,但是摆渡车往返一趟时间较短 , 那么中间一些间隔就可以缩短
比如:100 - 500 这段时间,往返时间只有10 那可以压缩至 100 - 120这段时间
为什么呢?因为如果你在120时刻发车,那么上一趟必须110时刻前必须发车,可以选择 100-110之间任何区间发车
为什么不选择 100之前呢, 假设99发车,那么 100-120这个区间的人必须等到120才能发车,而这个区间还可以继续发一辆车
所以压缩至两个往返时间即可 , 计算偏移量,排序后重新更新出发时间 即可。
优化方法2:斜率优化
*/
using namespace std;
const int N = 1e6+10;
int n, m, last, ti; // last:最后一个人到达车站的时间
int ans = 1e9;
int cnt[N]; // 第i时刻发车的人数
int sum[N]; //第i时刻所有人到达车站的时间前缀和
int f[N]; //第i时刻发车,当前等待的最少时间
int arr[N];//每个人发车的时刻
int solve(int n, int m) {
int maxT = 0;
for (int i = 1; i <= n; i++) {
scanf("%d", &arr[i]);
//maxT = max(maxT, arr[i]);
}
sort(arr+1, arr + n + 1);
int offset = 0;
int last = 0;
for(int i = 1; i <= n; i ++) {
if(arr[i] -last > 2 * m) {
offset += (arr[i] - last - 2 * m);
}
last = arr[i];
arr[i] -= offset;
}
// for(int i = 1; i <= n; i ++) {
// cout << arr[i] <<" ";
// }
for(int i = 1; i <= n; i ++) {
cnt[arr[i]]++;
maxT = max(maxT, arr[i]);
sum[arr[i]] += arr[i];
}
for (int i = 1; i < maxT + m; i++) {
cnt[i] += cnt[i - 1]; // 数量前缀和.
sum[i] += sum[i - 1]; // 时间前缀和
}
for (int i = 0; i < maxT + m; i++) {
f[i] = cnt[i] * i - sum[i]; // 特判边界情况.
for (int j = max(0, i - 2 * m); j <= i - m; j++) {
f[i] = min(f[i], f[j] + (cnt[i] - cnt[j]) * i - (sum[i] - sum[j]));
}
}
for (int i = maxT; i < maxT + m; i++) {
ans = min(ans, f[i]);
}
return ans;
}
int main() {
scanf("%d%d", &n, &m); // 读入数据
cout << solve(n, m) << endl;
}
// 朴素方法
//int main() {
// scanf("%d%d", &n, &m); // 读入数据
// for (int i = 1; i <= n; i++) {
// scanf("%d", &ti);
// last = max(last, ti);
// cnt[ti]++;
// sum[ti] += ti;
// }
// for (int i = 1; i < last + m; i++) {
// cnt[i] += cnt[i - 1]; // 数量前缀和.
// sum[i] += sum[i - 1]; // 时间前缀和
// }
// for (int i = 0; i < last + m; i++) {
// f[i] = cnt[i] * i - sum[i]; // 特判边界情况.
// for (int j = 0; j <= i - m; j++) {
// f[i] = min(f[i], f[j] + (cnt[i] - cnt[j]) * i - (sum[i] - sum[j]));
// }
// }
// for (int i = last; i < last + m; i++) {
// ans = min(ans, f[i]);
// }
// printf("%d\n", ans);
// return 0;
//}
题目
请参加比赛来查看题目。
- 状态
- 已结束
- 规则
- IOI
- 题目
- 4
- 开始于
- 2024-6-16 15:45
- 结束于
- 2024-7-3 7:45
- 持续时间
- 400 小时
- 主持人
- 参赛人数
- 15