试题 历届试题 地宫取宝(dfs、dp)

 

问题描述
  X 国王有一个地宫宝库。是 n x m 个格子的矩阵。每个格子放一件宝贝。每个宝贝贴着价值标签。

  地宫的入口在左上角,出口在右下角。

  小明被带到地宫的入口,国王要求他只能向右或向下行走。

  走过某个格子时,如果那个格子中的宝贝价值比小明手中任意宝贝价值都大,小明就可以拿起它(当然,也可以不拿)。

  当小明走到出口时,如果他手中的宝贝恰好是k件,则这些宝贝就可以送给小明。

  请你帮小明算一算,在给定的局面下,他有多少种不同的行动方案能获得这k件宝贝。
输入格式
  输入一行3个整数,用空格分开:n m k (1<=n,m<=50, 1<=k<=12)

  接下来有 n 行数据,每行有 m 个整数 Ci (0<=Ci<=12)代表这个格子上的宝物的价值
输出格式
  要求输出一个整数,表示正好取k个宝贝的行动方案数。该数字可能很大,输出它对 1000000007 取模的结果。
样例输入
2 2 2
1 2
2 1
样例输出
2
样例输入
2 3 2
1 2 3
2 1 5
样例输出
14
思路
1.记忆化dfs
很典型的搜索题目,开一个四维数组,分别存储位置和手上物品数量和最大价值,由于物品价值可以是0,初始化最大价值为-1;
本质是还是取和不取,更新各种状态,注意出口和记忆化即可。
#include<bits/stdc++.h>
using namespace std;
int n,m,k;;
long long int a[55][55][20][20];
int w[55][55];
const int inf=0x3f3f3f;
int def=1000000007;
int dfs(int x,int y,int c,int maxx)
{
    long long int ans=0;
    if(a[x][y][c][maxx+1]!=-1)return a[x][y][c][maxx+1];
    if(c>k)return 0;
    if(x==n-1&&y==m-1){
        if(c==k||c==k-1&&w[x][y]>maxx){
            ///ans++;
            return a[x][y][c][maxx+1]=1;
        }
        return a[x][y][c][maxx+1]=0;
    }
    if(x<n-1){
        if(w[x][y]>maxx){
            ans+=dfs(x+1,y,c+1,w[x][y]);
        }
        ans+=dfs(x+1,y,c,maxx);
    }
    if(y<m-1){
        if(w[x][y]>maxx){
            ans+=dfs(x,y+1,c+1,w[x][y]);
        }
        ans+=dfs(x,y+1,c,maxx);
    }
    return a[x][y][c][maxx+1]=ans%def;
}
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    memset(a,-1,sizeof(a));
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++)scanf("%d",&w[i][j]);
    }
    cout<<dfs(0,0,0,-1)%def<<endl;
}

 2.dp

dp[i][j][c][w]维护一个从左上角到ij点取了c件物品且最大的一项是w的方案数,每一项的方案数都是由其左边和上边的方案数转移而来。

此时我们把左边和上边的方案数都转移到当前位置。

分为选当前这件物品、不选当前物品,不选的话把所有相同状态转移即可。

选的话 需要把所有小于当前价值且物品数少一的方案累加。

#include<bits/stdc++.h>
using namespace std;
int dp[55][55][15][15];
int w[55][55];
int n,m,k;
const int mod = 1000000007;
int main(){
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>w[i][j];
            w[i][j]++;

        }
    }
    ///dp[i][j][c][w]维护一个从左上角到ij点取了k件物品且最大的一项是w的方案数
    dp[1][1][0][0]=1;
    dp[1][1][1][w[1][1]]=1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){///枚举每个点
            for(int c=0;c<=k;c++){///枚举件数
                for(int d=0;d<=13;d++){
                    dp[i][j][c][d]=(dp[i][j][c][d]+dp[i-1][j][c][d])%mod;
                    dp[i][j][c][d]=(dp[i][j][c][d]+dp[i][j-1][c][d])%mod;
                    if(c>0&&d==w[i][j]){
                        for(int f=0;f<w[i][j];f++){///选了当前物品
                            dp[i][j][c][d]=(dp[i][j][c][d]+dp[i-1][j][c-1][f])%mod;
                            dp[i][j][c][d]=(dp[i][j][c][d]+dp[i][j-1][c-1][f])%mod;
                        }
                    }
                }
            }
        }
    }
    long long res=0;
    for(int d=0;d<=13;d++){
        res+=dp[n][m][k][d];
        res%=mod;
    }
    cout<<res<<endl;
    return 0;
}

 

 

 

posted @ 2020-05-25 17:43  mohari  阅读(185)  评论(0编辑  收藏  举报