CF505C Mr. Kitayuta, the Treasure Hunter(DP) HQG_AC

CF505C Mr. Kitayuta, the Treasure Hunter

题意:

在一个有n个点的岛屿上,有n个点有宝藏。

现在给你一个d。

开始你在0

第一步,你将跳到d处

之后你可以选择跳c-1,c,c+1步(c为你上一步跳的步数)

每跳到某处可获得该地宝藏

询问最大宝藏数

这个题当Div.1 A,现场跪了不少人,各种大佬,神犇

解析:

很明显是个DP

Dp[i][j]表示在i,上一步跳了c步的最大值

但i<=30000,j<=30000,时空一起炸,How to do it?

考虑优化:

因为每次挑一部c都只会改变1,所以j有很多空间浪费

dp[i][j]表示在i,与d变化了j的最大值

通过等差数列求和,可轻松得知j最多变化300次

则这个题就变成水题了。。。

递推版

#include <bits/stdc++.h>
using namespace std ;
const int inf=0x3f3f3f3f ;
const int D=400 ; 
const int N=3e4 ;
int cnt[N+10];
int dp[N+10][2*D+10] ;
int n,d,x,ans ;
int main(){
    scanf("%d%d",&n,&d) ;
    for (int i=1;i<=n;i++){
        scanf("%d",&x);
        cnt[x]++ ;
    }
    for (int i=0;i<=N;i++){
        for (int j=-D;j<=D;j++)
        dp[i][j+D]=-inf ;
    }
    dp[d][D]=cnt[0]+cnt[d] ;
    for (int i=d;i<=N;i++){
        for (int j=-D;j<=D;j++){
            if (dp[i][j+D]==-inf){
                continue ;
            }
            for (int z=-1;z<=1;z++){
                int len=j+d+z ;
                if (j+z < -D || j+z >D || len<1 || i+len>N) continue ;
                dp[i+len][j+z+D]=max(dp[i+len][j+z+D],dp[i][j+D]+cnt[i+len]) ;
            }
        }
    }
    ans=0 ;
    for (int i=0;i<=N;i++){
        for (int j=-D;j<=D;j++){
            ans=max(ans,dp[i][j+D]) ;
        }
    }
    printf("%d\n",ans) ;
    return 0 ;
}

记忆化DP

#include <bits/stdc++.h>
using namespace std ;
#define N 30010
#define M 610
int s[N] ;
int dp[N][M] ;//滚动数组
//dp[i][j]表示跳到i,d的伏动范围为j-300的最大宝藏数 
//等差数列 告诉我们这大概最大有 sqrt(n)项≈300 
int n,d,x ;
bool ok(int x,int c){ //是否越界 
    int delta=d+c-300 ;
    if (delta<=0) return false ;
    if (delta+x>30000) return false ; 
    return true ;
}
int dfs(int x,int c){
    int &ret=dp[x][c] ;
    if (ret==-1){ //没算过 
        ret=0;
        if (ok(x,c)) ret=max(ret,dfs(x+d+c-300,c)) ;
        if (ok(x,c-1)) ret=max(ret,dfs(x+d+c-1-300,c-1)) ;
        if (ok(x,c+1)) ret=max(ret,dfs(x+d+c+1-300,c+1)) ;
        ret+=s[x] ;
    } 
    return ret ;
} 
int main(){
    scanf("%d%d",&n,&d);
    memset(dp,-1,sizeof(dp)) ;//清零 
    for (int i=1;i<=n;i++){
        scanf("%d",&x) ;
        s[x]++;//该点宝藏数 
    }
    printf("%d\n",dfs(d,300)) ;//滚动 
} 

猜你喜欢

转载自blog.csdn.net/hqg_ac/article/details/80999495