题目 1436: [蓝桥杯][2014年第五届真题]地宫取宝

一开始想的就是报暴搜,结果T了
然后网上寻得大佬的代码,开一个四维DP,就很灵性。

解题思路:
设d[i][j][k][c]为走到(i,j)的时候,手上共K个物品,最大价值小于c, 则d[i][j][k][c]转移方程是:
当前拿这个物品: s1= d[i-1][j][k-1][ w[i][j] ]+d[i][j-1][k-1][ w[i][j] ]

当前可以拿这个物品的条件是(c>a[i][j]&&k>0);

当前不拿物品 : s2= d[i-1][j][k][c]+d[i][j-1][k][c]
综合 d[i][j][k][c]= (s1+s2)%mod;

最大时间复杂度O(T)=O(ijkc)=O(50501313)=O(422500)

注意事项:

初始化(i == 0||j == 0)的时候d[i][j][k][c]=0;

临界条件( i== 1 && j == 1)当k!=0或者 k==1且c>a[i][j]的时候设置为1,其他时候0;
答案是d[n][m][k][12];

#include <iostream>
 
#define _for(i,a,b) for(int i=a;i<b;i++)
 
using namespace std;
typedef long long LL;
LL d[51][51][13][13],mod=1000000007;
int a[55][55];

int main() {
    int n,m,K;
    cin>>n>>m>>K;
    _for(i,0,n)_for(j,0,m)cin>>a[i+1][j+1];
    
    _for(i,1,n+1)_for(j,1,m+1)_for(k,0,K+1)_for(c,0,13){
        LL na=0,buna=0;
        if(i==1&&j==1){
            if(!k||(k==1&&c>a[i][j]))d[i][j][k][c]=1;
            continue;
        }
        if(k&&c>a[i][j]) na=d[i-1][j][k-1][a[i][j]]+d[i][j-1][k-1][a[i][j]];
        buna=d[i-1][j][k][c]+d[i][j-1][k][c];
        d[i][j][k][c]=na+buna;
        d[i][j][k][c]%=mod;
    }
    cout<<d[n][m][K][12]<<endl;
}

然后暴搜+记忆化搜索也能AC

#include<iostream>
#include<cstring>
using namespace std;
/* dp[x][y][num][v+1]和dfs(int x,int y,int num,int v) dp数组用于存储防止重复计算 
不用 dp[x][y][num][v]存储应为v必须初始化-1,
如果初始化0,当宝物价值为0时,map[x][y]==v,无法拾取宝物,
当为v=-1数组无法存,只能向后错一位存;
建模:
dfs(int x,int y,int num,int v)
点(x,y)到点(n,m)还要再取num个宝物且宝物必须大于v的取法种数;
边界:当x==n,y==m;已经到最后一个宝物;
1.num==0不需要再去宝物 返回1;
2.num==1&&map[n][m]>v 还要再取一个且能取 返回1
用数组记录并返回dp[x][y][num][v+1]=1;
递归方程:
dfs(x,y,num,v) = 
当v<=map[x][y] 该宝物能取 
dfs(x+1,y,num+1,map[x][y])+dfs(x+1,y,num,v)+dfs(x,y+1,num+1,map[x][y])+dfs(x,y+1,num,v);
当v>map[x][y] 该宝物不能取
dfs(x+1,y,num,v)+dfs(x,y+1,num,v);  
总结:根据题目条件求(1,1)到(n,m)取k件宝物的总走法
只能往右下走所以肯定能从(1,2)到(n,m) (2,1)到(n,m)取k-1件or k件宝物的走法中获得;
因为不是随便取,必须取大于先前取得的宝物价值 所以要一个变量v记录所以4个变量
则建立dfs(x,y,num,v) 
dfs(1,1,k,-1)=........
*/
const int N=1000000007;
int n,m,k;
int map[51][51];
int dp[51][51][13][14];
int dfs(int x,int y,int num,int v){//num代表还要取num个宝物 
    if(dp[x][y][num][v+1]!=-1)return dp[x][y][num][v+1];//能在记忆数组中找到 
    int t=0;
    if(x==n&&y==m){//边界条件 
        if(num==0)t++;
        else if(map[x][y]>v&&num==1)t++;
    }
    if(x+1<=n){//能下走 
        if(map[x][y]>v)t+=dfs(x+1,y,num-1,map[x][y]);
        t%=N;
        t+=dfs(x+1,y,num,v);    
        t%=N;     
    }
    if(y+1<=m){//能右走 
        if(map[x][y]>v)t+=dfs(x,y+1,num-1,map[x][y]);
        t%=N;
        t+=dfs(x,y+1,num,v);    
        t%=N;     
    }
    return dp[x][y][num][v+1]=t;//把下右的结果相加 
}
int main(){
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            cin>>map[i][j];
    memset(dp,-1,sizeof(dp));
    cout << dfs(1,1,k,-1);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值