zoukankan      html  css  js  c++  java
  • 过河(DP)

    问题描述】

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

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

    【输入文件】

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

    【输出文件】

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

    【样例输入】

    10
    2 3 5
    2 3 5 6 7

    【样例输出】

    2

    【数据规模】

      对于30%的数据,L <= 10000;

      对于全部的数据, L <= 10 0000 0000。

    题意:河上有桥,桥上有石子,青蛙需要沿着桥跳到河的另一侧。给出桥长L、青蛙跳的范围[s , t]、石子数量m及每个石子的位置a[i],求青蛙最少踩到的石子数。

    数据规模:1≤L≤101≤s≤t≤101M100

    策略:

    ①对于任一位置x , 只能由前面[x-t , x-s]这个范围的位置跳过来,因此只要先求出这些位置踩的石子数,找个最少的位置跳过来就ok。因此很容易想到动态转移方程:

    f[x]=min(f[x-j])+stone[x]   ( j∈[s , t])

    f[x]表示从桥头跳到x处需踩的最少石子数,stone[x]表示x处是否是石头(1表示是,0表示否)。

    ②桥长可达10亿,即状态数可达10亿。很显然,数组是不能开到这么大的,怎么办?由此我们想到,能否将状态数减少,又不影响结果呢?通过观察,题目中的石子数最多只有100,如果把这些石子数放到长度为10亿单位的桥上,那是多么的稀疏呀,这就为我们提供了可能。

    怎么样压缩?压缩后两个石子间距离保留多长才不影响结果呢?

     

    设第k个石子座标为x,第k-1个石子和第k个石子间距离足够大,则青蛙从两个石子间跳到第k个石子及之后的位置有:x、x+1、x+2、x+3……x+t-1。如果我们能保证,将石子k-1和石子k之间的距离缩短(即减少状态)后,青蛙依然能跳到这些位置,则可以平移。而这一点我们可以通过在两个石子间保留1个最小公倍数单位长度得到保证。

    ③注意特殊情况:当s=t时,只需考查石子是否是s的倍数即可。这种情况单独考查。

    #include<iostream>
    #include<algorithm>  //排序和求最小值要用到此文件。
    using namespace std;
    int L,s,t,m,ans;
    int a[110];  //保存石子位置
    int f[11000]={0};  //f[x]表示青蛙跳到位置i最少踏的石子数
    int stone[11000]={0}; //stone[x]表示位置x是否是石子,0表示不是,1表示是
    void solve()
    {
        int d(0),k=s*t,x;  //d表示累加平移量,k表示s和t的公倍数
        for (int i=1;i<=m+1;i++)
        {
            x=a[i]-d-a[i-1];  //x表示第i个石子和第i-1个石子的距离
            if (x>k) d+=x-k;  //超过公倍数部分用作平移
            a[i]=a[i]-d;
            stone[a[i]]=1;  //标记平移后位置是石子
        }
        stone[a[m+1]]=0; //桥尾不是石子
        f[0]=0;
        for (int i=1;i<=a[m+1]+t-1;i++)  //考查桥上到桥尾的所有位置
        {
            f[i]=105;
            for (int j=s;j<=t;j++) //在i的前一个位置中找一个经历石子最少的
                if (i>=j) f[i]=min(f[i],f[i-j]);
            f[i]+=stone[i];  //加上当前位置石子数
        }
        ans=101;
        for (int i=a[m+1];i<=a[m+1]+t-1;i++)  //在跳过桥后所有位置中找一个最小值
            ans=min(ans,f[i]);
        cout<<ans<<endl;
    }
    int main()
    {
        cin>>L>>s>>t>>m;
        ans=0;
        a[0]=0; a[m+1]=L;
        for (int i=1;i<=m;i++) cin>>a[i];
        sort(a+1,a+m+1);  //对桥中间石子位置排序,这上步必须要有
        if (s==t) {  //这种情况只需考查石子是否是石子的倍数即可
            for (int i=1;i<=m;i++)
                if (a[i]%s==0)
                    ans++;
            cout<<ans<<endl;
        }
        else solve();
        return 0;
    }

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    int l,s,t,m;
    int a[100+50];
    int stones[10000+50];
    int f[10000+50];
    
    
    int gcd(int a,int b)
    {
        if(b==0)
            return a;
        else
            return gcd(b,a%b);
    }
    int lcm(int a,int b)
    {
        return a*b/gcd(a, b);
    }
    int main ()
    {
        scanf("%d%d%d%d",&l,&s,&t,&m);
        for(int i=1;i<=m;i++)
            scanf("%d",&a[i]);
        a[0]=0;a[m+1]=l;
        sort(a,a+m+2);
        if(s==t)
        {
            int ans=0;
            for(int i=1;i<=m;i++)
                if(a[i]%s==0)
                    ans++;
            printf("%d
    ",ans);
        }
        else
        {
            int d=0,k=lcm(s,t);
            for(int i=1;i<=m+1;i++)
            {
                int x=a[i]-d-a[i-1];
                if(x>k)d+=x-k;
                a[i]-=d;
                stones[a[i]]=1;
            }
            stones[a[m+1]]=0;
            for(int i=1;i<=a[m+1]+t-1;i++)
            {
                f[i]=666;
                for(int j=s;j<=t;j++)
                    if(i>=j)
                        f[i]=min(f[i],f[i-j]);
                f[i]+=stones[i];
            }
            int ans=10000;
            for(int i=a[m+1];i<=a[m+1]+t-1;i++)
            {
                ans=min(ans,f[i]);
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
    



    想的太多,做的太少。
  • 相关阅读:
    Vue路由机制
    谷歌浏览器打不开应用商店的解决方法
    Vue报错——Component template should contain exactly one root element. If you are using vif on multiple elements, use velseif to chain them instead.
    Vue.js学习之——安装
    Vue使用axios无法读取data的解决办法
    关于localstorage存储JSON对象的问题
    2013年整体计划
    个人喜欢的警语收集
    Linux防火墙的关闭和开启
    Flex修改title 转载
  • 原文地址:https://www.cnblogs.com/pealicx/p/6115648.html
Copyright © 2011-2022 走看看