地宫寻宝

地宫取宝

 

    X国王有一个地宫宝库。是 nx 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


这里是没有经过提交成功的。一部分数据超时,有待优化。

#include <stdio.h>
#include <string.h>//无非就是四条路径:向下不拿,向右不拿,向下拿,向右拿(拿不拿都是针对当前点)。注意之中max的变化,和终点的判断。
int sum = 0, n, m, k, max, a[51][51];
void dfs(int i, int j, int x, int max)
{
    if(i > n || j > m || i < 1 || j < 1 || x > k)
        return;
   // printf("%d %d %d %d\n", i, j, x, sum);
    if(i == n && j == m)
    {
        if((a[i][j] > max && (x == k || x == k-1))||(a[i][j] <= max && x == k)) //到达终点时,对于终点这里的宝物拿不拿呢?
            sum++;        //如果这个点的宝物的值够大,拿不拿都行,那么当前的数目为k(不拿了)或k-1(要拿这点的宝物才能凑够k件)时,这条路线必为可行解。
        return;
    }
    dfs(i+1, j, x, max); //这里要注意,开始一直wa,就是这里的max是随着递归的层数变化而变化的,如果回溯到了上一层,那么max也应该是上一层的max,不能直接在语句里给max赋值。这样回溯之后就无法变化了(应该就像宝物数x的变化一样)。
    dfs(i, j+1, x, max);//如果选择不拿,max不变
    if(a[i][j] > max)
    {
        dfs(i+1, j, x+1, a[i][j]);//如果选择拿,那么下一个max就是这点的值
        dfs(i, j+1, x+1, a[i][j]);
    }
}
int main()
{
    memset(a, 0, sizeof(a));
    scanf("%d %d %d", &n, &m, &k);
    for(int i = 1 ; i <= n ; i++)
        for(int j = 1 ; j <= m ; j++)
    {
        scanf("%d", &a[i][j]);
    }
    dfs(1, 1, 0, -1);
    printf("%d\n", sum%1000000007);
    return 0;
}


 又做了做这道题,起初想用纯dp做,于是有了这样的代码:

#include <stdio.h>
#include <string.h>
#include <algorithm>
#define mod 1000000007
using namespace std;

int a[53][53];
int m[53][53][13];
long long dp[53][53][13];
int main()
{
	int n, mm, t, i, j, k;
	scanf("%d %d %d", &n, &mm, &t);
	for(i = 0 ; i < n ; i++)
	    for(j = 0 ; j < mm ; j++)
	    scanf("%d", &a[i][j]);
	memset(dp, 0, sizeof(dp));
	memset(m, -1, sizeof(m));
	dp[0][0][0] = 1;
	dp[0][0][1] = 1;
	m[0][0][0] = -1;
    m[0][0][1] = a[0][0];
	for(i = 1 ; i < mm ; i++)
	{
		for(k = 0 ; k <= t ; k++)
		{
			dp[0][i][k] = (dp[0][i][k] + dp[0][i - 1][k]) % mod;
			if(k > 0 && a[0][i] > m[0][i - 1][k - 1])
			{
		   		dp[0][i][k] = (dp[0][i][k] + dp[0][i - 1][k - 1]) % mod;
		    	m[0][i][k] = a[0][i];
			}
		}
	}
	for(i = 1 ; i < n ; i++)
	{
		for(k = 0 ; k <= t ; k++)
		{
			dp[i][0][k] = (dp[i][0][k] + dp[i - 1][0][k]) % mod;
			if(k > 0 && a[i][0] > m[i - 1][0][k - 1])
			{
		    	dp[i][0][k] = (dp[i][0][k] +  dp[i - 1][0][k - 1]) % mod;
		    	m[i][0][k] = a[i][0];
			}
		}
	}
	for(i = 1 ; i < n; i++)
	for(j = 1 ; j < mm; j++)
	{
		for(k = 0 ; k <= t ; k++)
		{
			dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j][k]) % mod;
			dp[i][j][k] = (dp[i][j][k] + dp[i][j - 1][k]) % mod;
			if(k > 0 && a[i][j] > m[i - 1][j][k - 1])
			{
		    	dp[i][j][k] = (dp[i][j][k] +  dp[i - 1][j][k - 1]) % mod;
		    	m[i][j][k] = a[i][j];
			}
			if(k > 0 && a[i][j] > m[i][j - 1][k - 1])
			{
		    	dp[i][j][k] = (dp[i][j][k] +  dp[i][j - 1][k - 1]) % mod;
		    	m[i][j][k] = a[i][j];
			}
		}
	}
	printf("%lld\n",dp[n - 1][mm - 1][t]);
	return 0;
}

看起来好像挺有道理的样子,样例也对。可就是ac不了。后来仔细想了想,突然发现,这里在某一个确定的位置上,如果拿了当前这个点的物品,那么最大值m肯定是这个点所对应地值这没问题,但如果不拿呢?它可以从四个可能的方法过来,那这个点对应的最大值应该是那个方法对应的呢?也就是说,这里的数组没办法唯一性地标识一个状态。

后来想了想,如果要唯一性标识状态,就得记录路径,而记录路径最好的办法就是搜索,在走的这一条路上,最大值是谁一目了然。然后和普通搜索的最大区别就是避免了重复子问题。dp[x][y][num][max]表示在xy这个点上,已经拿了num个物品,其中最大的为max,从这样的状态出发,一直到出口,物品个数恰好为k个的方案数。所以说一个dp标识的状态是唯一的,从一个确定的状态出发,到出口,这样的方案数是一定的。所以当再次搜索到这个状态的时候,不必继续向下搜索了,直接返回dp保存的值即可。

#include <stdio.h>
#include <string.h>
#define mod 1000000007
int n, m, k, a[53][53];
long long dp[53][53][13][16], ans;
int dfs(int x, int y, int num, int max)
{
	if(x >= n || y >= m)
	    return 0;
	if(dp[x][y][num][max] != -1)
		return dp[x][y][num][max];
	if(x == n - 1 && y == m - 1)
	{
		if(num == k || num == k - 1 && a[x][y] > max)
		    return dp[x][y][num][max] = 1;
		return dp[x][y][num][max] = 0;
	}
	long long t = 0;
	t = (t + dfs(x + 1, y, num, max)) % mod;
	t = (t + dfs(x, y + 1, num, max)) % mod;
	if(a[x][y] > max || max == 15)
	{
	    t = (t + dfs(x + 1, y, num + 1, a[x][y])) % mod;
	    t = (t + dfs(x, y + 1, num + 1, a[x][y])) % mod;
	}
	return dp[x][y][num][max] = t;
}
int main()
{
	int i, j;
	scanf("%d %d %d", &n, &m, &k);
	for(i = 0 ; i < n ; i++)
	for(j = 0 ; j < m ; j++)
	    scanf("%d", &a[i][j]);
	memset(dp, -1, sizeof(dp));
	ans = dfs(0, 0, 0, 15);//这里初始的max值应该为-1的,因为物品价值可以为0。但这样在dfs赋值dp数组的时候就会出现负数越界,所以就用15表示负数 
	printf("%lld\n", ans);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值