• Vijos 1002 过河 状态压缩DP


    描述

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

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

    对于30%的数据,L <= 10000;
    对于全部的数据,L <= 10^9。

    格式

    输入格式

    输入的第一行有一个正整数L(1 <= L <= 10^9),表示独木桥的长度。第二行有三个正整数S,T,M,分别表示青蛙一次跳跃的最小距离,最大距离,及桥上石子的个数,其中1 <= S <= T <= 10,1 <= M <= 100。第三行有M个不同的正整数分别表示这M个石子在数轴上的位置(数据保证桥的起点和终点处没有石子)。所有相邻的整数之间用一个空格隔开。

    输出格式

    输出只包括一个整数,表示青蛙过河最少需要踩到的石子数。

    样例1

    样例输入1

    10
    2 3 5
    2 3 5 6 7

    样例输出1

    2

    限制

    1s

    来源

    NOIp2005 第二题

    题解

    根据题目意思可以发现这是一道动态规划的问题。并且我们可以很快地列出他的状态转移方程如下:

    f[i] = min{f[i-j]}+a[i], (s<=j<=t)
    

    其中:
    f[i]表示到达第i点位置最少要踩到的石头数;
    a[i]表示第i点位置是否有石子,有则a[i]为1,否则a[i]为0。

    但是这样的话时间复杂度达到了O(L*T),会超时。

    这个时候我们回去考虑是否能够压缩一下数据,比如说L的大小。
    我们需要用到下面这个定理:
    对于公式

    P*x+(P+1)*y=Q
    

    其中x,y是未知数,P是跳跃的距离,P+1也是跳跃的距离,对于任意Q,Q>=P(P-1), x,y一定存在正整数解,换句话说当两个石子之间的距离大于等于T(T-1)时,中间有相当大的距离是每个点都可以跳到的,因为没有石子,所以对答案没有贡献,可以取模(%90)

    这样我们就成功的把很多没有用到的状态压缩了。这就是所谓的状态压缩DP。

    优化后的代码如下:

    #include <iostream>
    #include <algorithm>
    using namespace std;
    #define inf (1<<29)
    
    int l, s, t, m, f[10010], a[10010], p[110];
    
    int main()
    {
        cin >> l >> s >> t >> m;
        for (int i = 0; i < m; i ++)
            cin >> p[i];
        if (s == t)
        {
            int res = 0;
            for (int i = 0; i < m; i ++)
                if (p[i] % s == 0)
                    res ++;
            cout << res << endl;
            return 0;
        }
        sort(p, p + m);
        p[m] = l;
        p[0] %= 90;
        for (int i = 1; i <= m; i ++)
            p[i] = p[i-1] + (p[i] - p[i-1]) % 90;
        for (int i = 0; i < m; i ++)
            a[p[i]] = 1;
        f[0] = 0;
        for (int i = 1; i <= p[m]; i ++)
            f[i] = inf;
        for (int i = 1; i <= p[m]; i ++)
            for (int j = s; j <= t; j ++)
                if (i - j >= 0)
                    f[i] = min(f[i], f[i-j]+a[i]);
        cout << f[p[m]] << endl;
        return 0;
    }
    
    

    参考

  • 相关阅读:
    CSS margin的一些让你模糊的点
    VUE中CSS样式穿透
    如何给img标签里的请求添加自定义header?
    iframe的父子页面进行简单的相互传值
    Docker部署网站之后映射域名
    机器学习笔记(九)---- 集成学习(ensemble learning)【华为云技术分享】
    鲲鹏性能优化十板斧之前言 | 鲲鹏处理器NUMA简介与性能调优五步法
    【我的物联网成长记8】超速入门AT指令集【华为云技术分享】
    【直播分享】实现LOL小地图英雄头像分析案例【华为云分享】
    MongoDB一次节点宕机引发的思考(源码剖析)【华为云分享】
  • 原文地址:https://www.cnblogs.com/xianyue/p/6928381.html
Copyright © 2020-2023  润新知