题解:P1052 [NOIP 2005 提高组] 过河
P1052 [NOIP 2005 提高组] 过河
题目分析:
在长为
算法思路:
特殊情况:当
路径压缩:因为
状态转移:定义
关键代码:
if(a[i] - a[i-1] > 72) {
b[i] = b[i-1] + 72;
}
else {
b[i] = b[i-1] + (a[i] - a[i-1]);
}
flag[b[i]] = 1;
按照上文进行缩点操作,
AC代码如下:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=105, maxL=9e3+7; // 最大石头数和路径压缩后最大长度
ll flag[maxL]; // 标记压缩后坐标是否有石头
ll l, sum; // 河道总长,临时计数器
ll s, m, t; // 跳跃步数范围[s,t], 石头数,实际用t值
ll a[N], b[N]; // 原石头坐标,压缩后坐标
ll dp[maxL]; // dp[i]表示到达位置i的最小踩石数
signed main() {
cin >> l;
cin >> s >> t >> m;
for(int i=1; i<=m; i++) {
cin >> a[i];
}
// 处理特殊情况
if(s == t) {
for(int i=1; i<=m; i++) {
// 统计正好在s倍数位置的石头
if(a[i] % s == 0) sum++;
}
cout << sum;
return 0;
}
// 常规情况处理
sort(a+1, a+m+1); // 将石头位置排序
a[m+1] = l; // 添加终点为最后一个"虚拟石头"
// 路径压缩:将长距离空白压缩为固定长度
b[0] = 0; // 压缩后的起点仍为0
for(int i=1; i<=m+1; i++) {
// 当两石头间距大于72时,压缩为72(优化关键点)
if(a[i] - a[i-1] > 72) {
b[i] = b[i-1] + 72;
} else {
b[i] = b[i-1] + (a[i] - a[i-1]);
}
flag[b[i]] = 1; // 标记压缩后的石头位置(终点特殊处理)
}
flag[b[m+1]] = 0; // 终点位置不需要踩
// 初始化DP数组(初始化为极大值105,因最多100个石头)
for(int i=1; i<=b[m+1]+t; i++) {
dp[i] = 105;
}
// 动态规划核心:计算每个位置的最小踩石数
for(int i=s; i<=b[m+1]+t; i++) { // 遍历所有可达位置
for(int j=s; j<=t; j++) { // 枚举所有可能的跳跃步数
if(i-j >= 0) { // 确保前一位置合法
// 状态转移:从i-j位置跳j步到i,累加当前石头标记
dp[i] = min(dp[i], dp[i-j] + flag[i]);
}
}
}
// 寻找终点区域的最小值(可能跳过终点)
ll ans = 105;
for(int i=b[m+1]; i<=b[m+1]+t; i++) { // 终点及之后t个位置
ans = min(ans, dp[i]);
}
cout << ans;
return 0;
}
谢谢收看