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

  • 相关阅读:
    Running APP 使用说明
    Android 控件八 WebView 控件
    Android 控件七 ImageView 控件
    Android 控件六 CheckBox 控件
    Android 控件五 RadioButton 控件
    Android 控件四 EditText 控件
    Android 控件三 TextView 控件实现 Button
    Android 控件二 Button
    Android 基础控件演示实例
    Android 控件一 TextView
  • 原文地址:https://www.cnblogs.com/zeze/p/6159682.html
Copyright © 2011-2022 走看看