zoukankan      html  css  js  c++  java
  • 算法实验题 5.2 游戏

    ★问题描述
    G 参加了一场游戏,游戏中有 20001 个格子(编号为 0~20000),小 G 初始时在编
    号为 0 的格子上。小 G 可以按照以下规则在格子上移动。
    1、 开始时,小 G 从编号为 0 的格子上移动到编号为 d 的格子上
    2、 用 L 来表示上一次移动的距离,假设上一次移动中小 G 从编号 pre 的格子移动到编号
    cur 的格子,则 L=pre-cur。下一次移动中小 G 只能向前移动 L-1LL+1 步,即小 G
    能到达编号为( cur+L-1)、( cur+L)、( cur+L+1)的格子。因为小 G 只能向前走不能停
    留,所以当 L=1 时,只能移动 1 步或者 2 步,不能移动 0 步。
    3、 当不存在可到达的格子时,游戏结束。
    有的格子上会有一些宝石,小 G 站在编号为 i 的格子上时,则可以获得该格子上的宝石。
    G 想知道游戏结束时,他最多可以获得多少宝石,请你帮助他。
    ★数据输入
    输入的第一行为包括两个整数 n,d(1≤ n≤ 20000;1≤ d≤ 300)表示有 n 个格子上存在
    宝石,第一次移动是从 0 移动到 d
    接 下 来 n 行 , 每 行 包 括 一 个 整 数 。 第 i(1 ≤ i ≤ n) 行 的 数
    pi (d ≤ p1 ≤ p2 ≤ ... ≤ pn ≤ 20000)表示编号为 pi 的格子上存在 1 个宝石。
    30%的数据 n<=100
    70%的数据 n<=1000
    100%的数据 n<=20000.

     

    解法:

    容易想到DP,dp[i][j]表示到达 i 处,现在步长为 j 时最多收集到的财富,转移也不难,,cnt[i]表示 i 处的财富。

    但由于n太大,用n*n的时间内存是不可以接受的

    步长直接开20000存的话肯定是不行的,发现其实走过20000之前,步长的变化不会很大

    若每一步以d+1的步长走,到达20000这个位置,则总的步长为:d+1+d+2+d+3+.......+d+n>=1+2+3+.....+n=n·(n+1)/2>=20000 解得n<=200

    即步长变化不会超过200.所以第二维保存相对原始步长的改变量,-200~200,开400就够了,这样就不会MLE了。

    求以d为起点的最优路径,应该从后往前dp。

    dp[i][j]表示到达 i 处,先前的步长变化为j时最多收集到的财富。

    步长变化:j

    步长:L=d+j

    从后往前dp如下 :
    dp[i][j]=0当i大于n(总格子的编号)
    dp[i][j]=i格获得的宝石+max(dp[i+L][j],dp[i+L+1][j+1]) 当j==1&&i<=n
    dp[i][j]=i格获得的宝石+max(dp[i+L][j],dp[i+L+1][j+1],dp[i+L-1][j-1]) 当j>1&&i<=n

    步长的变化加一个偏移量:offset=200

    dp[i][j+offset]=0当i大于n(总格子的编号)
    dp[i][j+offset]=i格获得的宝石+max(dp[i+L][j+offset],dp[i+L+1][j+1+offset]) 当j==1&&i<=n
    dp[i][j+offset]=i格获得的宝石+max(dp[i+L][j+offset],dp[i+L+1][j+1+offset],dp[i+L-1][j-1+offset]) 当j>1&&i<=n

    解法二:

    题意:0~30000有30001个地方,每个地方有一个或多个金币,第一步走到了d,步长为d,以后走的步长可以是上次步长+1,-1或不变,走到某个地方可以收集那个地方的财富,现在问走出去(>30000)之前最多可以收集到多少财富。

    解法:容易想到DP,dp[i][j]表示到达 i 处,步长变化为 j 时最多收集到的财富,转移也不难,a[i]表示 i 处的财富。

    step=d+j

    dp[i+step-1][j-1]= max(dp[i+step-1][j-1],dp[i][j]+a[i+step+1])

    dp[i+step][j] = max(dp[i+step][j],dp[i][j]+a[i+step])

    dp[i+step+1][j+1] = max(dp[i+step+1][j+1],dp[i][j]+a[i+step+1])

    j加了一个偏移量:step=d+j-250

    步长直接开20000存的话肯定是不行的,发现其实走过20000之前,步长的变化不会很大

    若每一步以d+1的步长走,到达20000这个位置,则总的步长为:d+1+d+2+d+3+.......+d+n>=1+2+3+.....+n=n·(n+1)/2>=20000 解得n<=200

    即步长变化不会超过200.所以第二维保存相对原始步长的改变量,-200~200,开400就够了,这样就不会MLE了。

    #include<stdio.h>
    #include<string.h>
    int num[30001],dp[30001][1001];
    int max(int a,int b){return a>b?a:b;}
    int main(){
        int n,d,x,i,j,ans;
        scanf("%d %d",&n,&d);
        for(i=0;i<n;i++)
            scanf("%d",&x),num[x]++;
        memset(dp,-1,sizeof(dp));
        dp[d][500]=num[d];//初始化
        ans=0;
        for(i=d;i<=30000;i++){
            for(j=0;j<=1000;j++){//带偏移的步长变化
                if(dp[i][j]==-1)
                    continue;//判断第i个格子能否可达,如果可达,才可以往后跳
                ans=max(ans,dp[i][j]);
                int L=d+j-500;//实际步长
                if(L-1>0&&i+L-1<=30000)//不能移动0步
                    dp[i+L-1][j-1]=max(dp[i+L-1][j-1],dp[i][j]+num[i+L-1]);
                if(i+L<=30000)
                    dp[i+L][j]=max(dp[i+L][j],dp[i][j]+num[i+L]);
                if(i+L+1<=30000)
                    dp[i+L+1][j+1]=max(dp[i+L+1][j+1],dp[i][j]+num[i+L+1]);
            }
        }
        printf("%d
    ",ans);
        return 0;
    }
    /*
    dp[i][j]表示到达i处,步长为j时最多收集到的财富,a[i]表示i处的财富。
    dp[i+step-1]=max(dp[i+step-1],dp[i][j]+cnt[i+step+1])
    dp[i+step]=max(dp[i+step],dp[i][j]+cnt[i+step])
    dp[i+step+1]=max(dp[i+step+1],dp[i][j]+cnt[i+step+1])
    */
    #include<stdio.h>
    #include<string.h>
    int n,d;
    int a[61000],f[61000][802];
    int max(int a,int b){
        return a>b?a:b;
    }
    int main(){
        int k,i,j,t;
        scanf("%d %d",&n,&d);
        for (i=1;i<=n;i++){
            scanf("%d",&t);
            a[t]++;
        }
        for (i=30000;i>=0;i--){
            for (j=1;j<=800;j++){
                k=j+d-400;
                if (k==1)
                    f[i][j]=max(f[i+1][j]+a[i+1],f[i+1][j+1]+a[i+1]);
                if (k>1)
                    f[i][j]=max(max(f[i+k][j-1]+a[i+k],f[i+k][j+1]+a[i+k]),f[i+k][j]+a[i+k]);
            }
        }
        printf("%d",f[0][400]);
        return 0;
    }
    #include<stdio.h>
    #include<string.h>
    int b[30005][1100];
    int a[30005];
    int n,d,maxn;
    int max(int a,int b){
        return a>b?a:b;
    }
    int dp(int i,int j){
        int k;
        k=j-d+500;
        if(i>maxn)
            return 0;
        else if(j==0)
            return 0;
        else if(b[i][k]>=0)
            return b[i][k];
        else
            return b[i][k]=max(max(dp(i+j+1,j+1),dp(i+j,j)),dp(i+j-1,j-1))+a[i];
    }
    int main(){
        maxn=0;
        scanf("%d %d",&n,&d);
        memset(a,0,sizeof(a));
        memset(b,-1,sizeof(b));
        for(int i=0;i<n;i++){
            int t;
            scanf("%d",&t);
            if(maxn<t)
                maxn=t;//结束的位置
            a[t]++;//这个岛的宝石数量,
        }
        printf("%d
    ",dp(d,d));
        return 0;
    }

     http://www.icekrn.com/?p=609

  • 相关阅读:
    Mybatis源码中最重要的几个类
    学习爬虫-运营商积分
    IntelliJ IDEA 最新版 2019.2.4 激活 (持续更新)(含windows和Mac)
    归并排序之求小和
    归并排序
    理解递归
    插入排序
    对数器
    冒泡排序
    mysql 数据库名称,中间带有中划线问题
  • 原文地址:https://www.cnblogs.com/zeze/p/6159682.html
Copyright © 2011-2022 走看看