蓝桥杯--DP2 AcWing 1212. 地宫取宝

AcWing 1212. 地宫取宝

X 国王有一个地宫宝库,是 n×m 个格子的矩阵,每个格子放一件宝贝,每个宝贝贴着价值标签。
地宫的入口在左上角,出口在右下角。
小明被带到地宫的入口,国王要求他只能向右或向下行走。
走过某个格子时,如果那个格子中的宝贝价值比小明手中任意宝贝价值都大,小明就可以拿起它(当然,也可以不拿)。
当小明走到出口时,如果他手中的宝贝恰好是 k 件,则这些宝贝就可以送给小明。
请你帮小明算一算,在给定的局面下,他有多少种不同的行动方案能获得这 k 件宝贝。
输入格式
第一行 3 个整数,n,m,k,含义见题目描述。
接下来 n 行,每行有 m 个整数 Ci 用来描述宝库矩阵每个格子的宝贝价值。
输出格式
输出一个整数,表示正好取 k 个宝贝的行动方案数。
该数字可能很大,输出它对 1000000007 取模的结果。
数据范围
1≤n,m≤50,
1≤k≤12,
0≤Ci≤12
输入样例1:
2 2 2
1 2
2 1
输出样例1:
2
输入样例2:
2 3 2
1 2 3
2 1 5
输出样例2:
14

题意:给出各宝物的权值及坐标,每次只能取大于手中所有宝物权值的宝物,求出从左上走到右下终点时,手中宝物正好为k件的方案数

本来dp学的就不明白,一看题解四维的dp直接懵逼,不过分析出来其实还好,主要难点在能够想出这四维并对它们定义

先给出dp[ i ][ j ][ cnt ][ c ]
首先对坐标进行处理,定义i j为一二维,接下来考虑一个限定条件,由于题目要求到终点时手中物品必须为定值,由此定义cnt维来记录当前手中的物品数量;另一限定条件为每次只能拿起大于手中全部权值的宝物,再定义c维记录手中宝物权值的最大值

由此,dp[ i ][ j ][ cnt ][ c ]表达的含义即是:在i j坐标上,手中宝物数量为cnt,且宝物权值的最大值为c的全部方案数

分析dp的组成为4部分
A 从上向下走至此位置,且不拿当前宝物
B 从左向右走至此位置,且不拿当前宝物
C 从上向下走至此位置,且拿起当前宝物
D 从左向右走至此位置,且拿起当前宝物

由于是方案数的累计,在状态处理中要累加,并进行取模处理

其中AB情况分析较简单,只需从上一步转移状态即可

dp[i][j][cnt][c]=(dp[i][j][cnt][c]+dp[i-1][j][cnt][c])%mod;
dp[i][j][cnt][c]=(dp[i][j][cnt][c]+dp[i][j-1][cnt][c])%mod;

CD情况则较复杂,以C情况为例,选择当前坐标物品时,可以确定前三维分别是i-1,j,cnt-1,但当考虑到最大值时,当前dp值实则是一个集合,任意一个小于当前坐标权值的宝物都是方案数之一,由于题目中权值的限定很小,在这里可以遍历一遍所有取值的可能,将小于c的宝物方案数都归到其中,使用一个循环实现

for(int c=0;c<v;c++) {
	//u为cnt的枚举,v为最大值的枚举,找出所有小于v的值c
	dp[i][j][u][v]=(dp[i][j][u][v]+dp[i-1][j][u-1][c])%mod;
	dp[i][j][u][v]=(dp[i][j][u][v]+dp[i][j-1][u-1][c])%mod;
}

当所有情况计算结束后,将坐标为i j,且数量为k,各最大值的方案求和,即是最终结果

import java.io.*;
import java.util.*;

public class Main {
	static Scanner tab = new Scanner(System.in);
	static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
	static int N = 55;

	
	public static void main(String[] args) throws IOException {		
		int mod=(int)1e8+7;
		int w[][]=new int [N][N];
		int dp[][][][]=new int [N][N][13][14];
		//dp[i][j][cnt][c]  坐标i 坐标j cnt个物品  最大值c
		int n=tab.nextInt();
		int m=tab.nextInt();
		int k=tab.nextInt();
		for(int i=1;i<=n;i++) {
			for(int j=1;j<=m;j++) {
				w[i][j]=tab.nextInt();
				w[i][j]++;//设置偏移量,空出0位方便构造dp
			}
		}
		//第一件宝物拿与不拿的两种初始化
		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 u=0;u<=k;u++) {
					for(int v=0;v<=13;v++) {
						//不取当前坐标物品
						dp[i][j][u][v]=(dp[i][j][u][v]+dp[i-1][j][u][v])%mod;
						dp[i][j][u][v]=(dp[i][j][u][v]+dp[i][j-1][u][v])%mod;
						//取
						if(v==w[i][j]&&u>0) {
							//取得当前宝物权值为最大值
							//因为至少已经拿了当前物品,cnt不为0
							for(int c=0;c<v;c++) {
								dp[i][j][u][v]=(dp[i][j][u][v]+dp[i-1][j][u-1][c])%mod;
								dp[i][j][u][v]=(dp[i][j][u][v]+dp[i][j-1][u-1][c])%mod;
							}
						}
					}
				}
			}
		}
		int res=0;
		for(int i=0;i<=13;i++) {
			res=(res+dp[n][m][k][i])%mod;
		}
		System.out.println(res);
		
	}
}


一题敲一天系列

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值