zoukankan      html  css  js  c++  java
  • 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;
    }
    
    

    参考

  • 相关阅读:
    ExecutorService与Executors例子的简单剖析
    java中线程队列BlockingQueue的用法
    mysql常用语句
    dubbo+zookeeper例子
    大型网站架构系列:消息队列(二)
    大型网站架构系列:分布式消息队列(一)
    什么是Code Review(转)
    Ehcache 整合Spring 使用页面、对象缓存
    java 多线程——quartz 定时调度的例子
    Dubbo与Zookeeper、SpringMVC整合和使用(负载均衡、容错)
  • 原文地址:https://www.cnblogs.com/xianyue/p/6928381.html
Copyright © 2011-2022 走看看