离散化dp

过河

https://ac.nowcoder.com/acm/problem/16655

题目描述:

在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧。在桥上有一些石子,青蛙很讨厌踩在这些石子上。由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上的一串整点:0,1,……,L(其中L是桥的长度)。坐标为0的点表示桥的起点,坐标为L的点表示桥的终点。青蛙从桥的起点开始,不停的向终点方向跳跃。一次跳跃的距离是S到T之间的任意正整数(包括S,T)。当青蛙跳到或跳过坐标为L的点时,就算青蛙已经跳出了独木桥。

题目给出独木桥的长度L,青蛙跳跃的距离范围S,T,桥上石子的位置。你的任务是确定青蛙要想过河,最少需要踩到的石子数。

思路:

很显然是dp

状态是到达位置 i 时踩的石头的数量最少值

dp[i] = min(dp[i], dp[i - j] + vis[i])

其中vis[i] 指的是位置 i 是否有石头,有则为1,无则为0

再猫一眼题目范围,L<=1e9,不仅数组开不下,时间上也饶不了你

再猫一眼,发现石头的数量很少,暗示我们可以进行离散化,常见的离散化是通过map进行映射,但是这个题离散化的是石头间的距离

对于两个石头x,y,如果他们的距离大于lcm(s, t)时,则大于lcm(s,t)的所有点都可以到达,这个是可以证明的不过我不会证o(︶︿︶)o

还可以再简单一点,就是如果你懒得写lcm的板子的话,那直接让大于s * t的点之间的距离等于s * t即可

对于s = t的情况要进行特判

还有就是因为跳过了终点也算过了,那我们就让循环的右边界为st[n] + s * t

#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
#include <stdlib.h>
#include <sstream>
#include <map>
#include <set>
using namespace std;
#define inf 0x3f3f3f3f
#define MAX 100000 + 50
#define endl '\n'
#define seed 13331
#define mod 1000000007
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define mem(a,b) memset((a),(b),sizeof(a))
typedef  long long ll ;
//不开longlong见祖宗!

int l, n, s, t;
int tr[MAX];
int vis[MAX];
int st[MAX];
int dp[MAX];

int main(){
    cin>>l>>s>>t>>n;
    for(int i = 1; i <= n; ++i)cin>>tr[i];
    sort(tr + 1, tr + 1 + n);
    if(s == t){
        int cnt = 0;
        for(int i = 1; i <= n; ++i)if(tr[i] % s == 0)++cnt;
        cout<<cnt<<endl;
        return 0;
    }
    int k = s * t;
    st[0] = 0;
    for(int i = 1; i <= n; ++i){
        if(tr[i] - tr[i - 1] >= k)st[i] = st[i - 1] + k;
        else st[i] = st[i - 1] + tr[i] - tr[i - 1];
        vis[st[i]] = 1;
    }
    int r = st[n] + k;
    mem(dp, inf);
    dp[0] = 0;
    for(int i = 1; i <= r; ++i){
        for(int j = s; j <= t; ++j){
            if(i >= j)dp[i] = min(dp[i], dp[i - j] + vis[i]);
        }
    }
    int ans = 1e9;
    for(int i = st[n]; i <= r; ++i)ans = min(dp[i], ans);
    cout<<ans<<endl;
    return 0;
}
动态规划 文章被收录于专栏

dp的专项训练

全部评论

相关推荐

03-12 15:35
嘉应学院 Python
快说谢谢牛牛精灵:说不定就是下一个寒武纪!
点赞 评论 收藏
分享
上周组里招人,我面了六个候选人,回来跟同事吃饭的时候聊起一个让我挺感慨的现象。前三个候选人,算法题写得都不错。第一道二分查找,五分钟之内给出解法,边界条件也处理得干净。第二道动态规划,状态转移方程写对了,空间复杂度也优化了一版。我翻他们的简历,力扣刷题量都在300以上。后三个呢,就有点参差不齐了。有的边界条件没处理好,有的直接说这道题没刷过能不能换个思路讲讲。其中有一个女生,我印象特别深——她拿到题之后没有马上写,而是先问我:“面试官,我能先跟你确认一下我对题目的理解吗?”然后她把自己的思路讲了一遍,虽然最后代码写得不是最优解,但整个沟通过程非常顺畅。这个女生的代码不是最优的,但当我问她“如果这里是线上环境,你会怎么设计’的时候,她给我讲了一套完整的方案——异常怎么处理、日志怎么打、怎么平滑发布。她对这是之前在实习的时候踩过的坑。”我在想LeetCode到底在筛选什么?我自己的经历可能有点代表性。我当年校招的时候,也是刷了三百多道题才敢去面试。那时候大家都刷,你不刷就过不了笔试关。后来工作了,前三年基本没再打开过力扣。真正干活的时候,没人让你写反转链表,也没人让你手撕红黑树。更多的是:这个接口为什么慢了、那个服务为什么OOM了、线上数据对不上了得排查一下。所以后来我当面试官,慢慢调整了自己的评判标准。算法题我还会出,但目的变了。我出算法题,不是想看你能不能背出最优解。而是想看你拿到一个陌生问题的时候,是怎么思考的。你会先理清题意吗?你会主动问边界条件吗?你想不出来的时候会怎么办?你写出来的代码,变量命名乱不乱、结构清不清楚?这些才是工作中真正用得到的能力。LeetCode是一个工具,不是目的。它帮你熟悉数据结构和常见算法思路,这没问题。但如果你刷了三百道题,却说不清楚自己的项目解决了什么问题、遇到了什么困难、你是怎么解决的,那这三百道题可能真的白刷了。所以还要不要刷LeetCode?要刷,但别只刷题。刷题的时候,多问自己几个为什么:为什么用这个数据结构?为什么这个解法比那个好?如果换个条件,解法还成立吗?把刷题当成锻炼思维的方式,而不是背答案的任务。毕竟面试官想看到的,从来不是一台背题机器,而是一个能解决问题的人。
牛客51274894...:意思是光刷力扣还不够卷
AI时代还有必要刷lee...
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务