题目网址:http://lx.lanqiao.org/problem.page?gpid=T120

问题描述
  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



题目的意思很简单,就是用深搜的方式来找出到底有多少路径,但是开始的时候虽然一眼就认定了是深度搜索,但是还真的不知道怎么搜,因为碰到不能捡的物品就不知道该怎么处理了,不深搜吧,肯定之后有符合条件的,这样的话就会落掉很多情况,但是搜的话好像也是不知道该如何是好,最后我甚至是想到把不管什么情况都深搜,如果能捡起来,就都捡起来然后在改变这个点出现的最大值,之后查找这么多种选择出k个所构成的组合数来解决,但是之后想到这样的话,如果拿了这个物品,由于修改了其最大值,这样的话会对之后的选择造成影响,利用组合数来实现根本是不行的,这样的话这条思路又中断了。。。

贴一下开始结果不正确的代码,长一下记性:

#include <iostream>
#include <cstdio>
#include <cstring>
#define MAXN 1100
#define MOD 100000007
using namespace std;
int count = 0,sum=0;
int m,n,k, a[55][55];
long long gcd ( long long x,long long y){
if(y==0) return x>y?x:y;
int t=x%y;
while(t){ x=y,y=t,t=x%y; }
return y;
}
long long C(int a,int b){
long long mu=1,zi=1;
long long int n=b<a-b?b:a-b;
for(int i=1,te=a;i<=n;i++,te--){
mu*=te;
zi*=i;
long long int temp = gcd(mu,zi);
mu/=temp;
zi/=temp;
}
return mu/zi;
}
void dfs(int i,int j,int max){
if(i==m-1&&j==n-1) {
int mark=0;
if(a[i][j]>max)
count++,mark=1;
if(count>=k){ sum=(sum+C(count,k))%MOD ; }//   cout<<count<<' ';
if(mark)
count--;
return ;
}
int mark=0;
if(a[i][j]>max) { max=a[i][j],count++,mark=1; }
if(i+1<m)
dfs(i+1,j,max);
if(j+1<n)
dfs(i,j+1,max);
if(mark)
count--;
}
int main()
{
scanf("%d%d%d",&m,&n,&k);
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
scanf("%d",&a[i][j]);
dfs(0,0,0);
cout<<sum;
return 0;
}

之后才发现可以用动态规划的形式来深搜,比如这道题的话就是

这里的V是指当前所走的路径中的物品价值的最大值,这样的话就完全可以通过不同状态的转换来完成深搜了,好像理解起来也是比较合乎情理的,事实证明这样的思路也是对的。。。



(PS:现在递归起来好像还不习惯用传参数的方式来做判断,用一个全体变量这样的话每次都会在函数开始的时候修改,在函数退出的时候再还原,突然感觉这样写出来的程序就和多层循环的嵌套一样了,完全没有运用到递归中局部变量的优越性,以后用递归的时候还是判断有哪些状态会影响到深度搜索的路径,就直接把他加为传参变量,直接用于状态判断,(这也是接下来所运用的记忆搜索=深搜+dp),这样思路应该会更加清晰了 吧,虽然现在才发现这个问题有点白痴,但是。。。好像也没有什么但是了吧

#include <stdio.h>
#include <string.h>
#define N 1000000007
int n,m,k;
int map[50][50]; int dfs(int x,int y,int num,int max)//当前位置 拥有宝物的数量 拥有的宝物的最大值
{
if(x==n&&y==m){//到达出口
if( num==k||(num==k-1&&max<map[x][y]) ) //到达右下角,(1)可能不算右下角的也正好,(2)如果右下角的比当前的大,算上这一个如果正好也可以增加路径
return 1; //满足条件 当前点到目标有1种方案
else
return 0;//不满足条件 当前点到目标有0种方案
}
long long s=0;
if(x+1<=n){ //可以向下走
if (max<map[x][y])
s+=dfs(x+1,y,num+1,map[x][y]);
s+=dfs(x+1,y,num,max);
}
if(y+1<=m){ //可以向右走
if (max<map[x][y])
s+=dfs(x,y+1,num+1,map[x][y]);
s+=dfs(x,y+1,num,max);
}
return s%N;
}
int main(){
scanf("%d%d%d",&n,&m,&k);
for (int i = 1;i<=n;i++)//初始地宫
for (int j = 1; j <=m; j++)
scanf("%d",&map[i][j]);
printf("%d", dfs(1,1,0,-1));
return 0;
}

提交贴图:





果然还是完美的TL了,原因好像很简单了,因为深搜本来就是一件在时间复杂度上很消耗时间的算法了,现在再由于状态的区分明显复杂(每次进入一种状态最坏需要按照四条路径搜索,这可以在状态转移的方程中明显的看出来),这样的话好像就更加浪费了,所以果断超时。。。



这样的话就引出下面一个从来没用用到过的模型了。。。

第一次做记忆搜索的题目,开始的时候还不太清楚用数组干什么,后来才发现是用来记忆状态的、



先贴一下百度上的名词解释:

记忆化搜索:算法上依然是搜索的流程,但是搜索到的一些解用动态规划的那种思想和模式作一些保存。
一般说来,动态规划总要遍历所有的状态,而搜索可以排除一些无效状态。
更重要的是搜索还可以剪枝,可能剪去大量不必要的状态,因此在空间开销上往往比动态规划要低很多。
记忆化算法在求解的时候还是按着自顶向下的顺序,但是每求解一个状态,就将它的解保存下来,
以后再次遇到这个状态的时候,就不必重新求解了。
这种方法综合了搜索和动态规划两方面的优点,因而还是很有实用价值的。

结合这道题来说就是在碰到以前用过的状态的时候直接用打出的表的值就好了,这样的话就节约了不少时间了。。。

(这个题好像长了不少见识,虽然用来好长时间才做出来。。。)

#include <stdio.h>
#include <string.h>
#define N 1000000007
int n,m,k;
int map[50][50];
int dp[50][50][15][15];//dp数组中记录的是状态 xy坐标 拥有宝物数量 拥有宝物的最大值(这4个可以详尽唯一的描述没一种可能)
//如dp[3][4][5][6]=7 即当在map[3][4]且身上有5件宝物 宝物的最大值是6 是到达终点有7中路径 int dfs(int x,int y,int num,int max)//当前位置 拥有宝物的数量 拥有的宝物的最大值
{
if (dp[x][y][num][max+1]!=-1)//判断这个状态是否已经走过,如果走过就直接用记录的数值计算//因为宝物有可能为0所以定义max时用最小值-1 这就导致无法作为下标使用 实际上如果测试数据中宝物价值没有0
//将所有的+1 去掉也是可以的 这里的话如果去掉肯定是有些数据不对的,不信可以提交试一下,根本过不了
return dp[x][y][num][max+1];
//记忆化的记忆就指的是上面 if(x==n&&y==m){//到达出口
if( num==k||(num==k-1&&max<map[x][y]) ) //到达右下角,(1)可能不算右下角的也正好,(2)如果右下角的比当前的大,算上这一个如果正好也可以增加路径
return dp[x][y][num][max+1]=1; //满足条件 当前点到目标有1种方案
else
return dp[x][y][num][max+1]=0;//不满足条件 当前点到目标有0种方案
}
long long s=0;
if(x+1<=n){ //可以向下走
if (max<map[x][y])
s+=dfs(x+1,y,num+1,map[x][y]);//当前位置 拥有宝物的数量 拥有的宝物的最大值
s+=dfs(x+1,y,num,max); //当前位置 拥有宝物的数量 拥有的宝物的最大值
}
if(y+1<=m){ //可以向右走
if (max<map[x][y])
s+=dfs(x,y+1,num+1,map[x][y]);//当前位置 拥有宝物的数量 拥有的宝物的最大值
s+=dfs(x,y+1,num,max); //当前位置 拥有宝物的数量 拥有的宝物的最大值
}
return dp[x][y][num][max+1]=s%N;
}
int main(){
scanf("%d%d%d",&n,&m,&k);
for (int i = 1;i<=n;i++)//初始地宫
for (int j = 1; j <=m; j++)
scanf("%d",&map[i][j]);
memset(dp,-1,sizeof(dp));
dfs(1,1,0,-1);
printf("%d",dp[1][1][0][0]); return 0;
}

因此记忆搜索时从后向前搜索的。。。

蓝桥杯---地宫取宝(记忆搜索=搜索+dp)的更多相关文章

  1. 蓝桥杯历届试题 地宫取宝 dp or 记忆化搜索

    问题描述 X 国王有一个地宫宝库.是 n x m 个格子的矩阵.每个格子放一件宝贝.每个宝贝贴着价值标签. 地宫的入口在左上角,出口在右下角. 小明被带到地宫的入口,国王要求他只能向右或向下行走. 走 ...

  2. 【蓝桥杯真题】地宫取宝(搜索->记忆化搜索详解)

    链接 [蓝桥杯][2014年第五届真题]地宫取宝 题目描述 X 国王有一个地宫宝库.是 n x m 个格子的矩阵.每个格子放一件宝贝.每个宝贝贴着价值标签. 地宫的入口在左上角,出口在右下角. 小明被 ...

  3. 蓝桥杯 2014本科C++ B组 地宫取宝 DFS+记忆化搜索

    历届试题 地宫取宝   时间限制:1.0s   内存限制:256.0MB 问题描述 X 国王有一个地宫宝库.是 n x m 个格子的矩阵.每个格子放一件宝贝.每个宝贝贴着价值标签. 地宫的入口在左上角 ...

  4. 2014 蓝桥杯 预赛 c/c++ 本科B组 第九题:地宫取宝(12') [ dp ]

      历届试题 地宫取宝   时间限制:1.0s   内存限制:256.0MB     锦囊1   锦囊2   锦囊3   问题描述 X 国王有一个地宫宝库.是 n x m 个格子的矩阵.每个格子放一件 ...

  5. 地宫取宝|2014年蓝桥杯B组题解析第九题-fishers

    地宫取宝 X 国王有一个地宫宝库.是 n x m 个格子的矩阵.每个格子放一件宝贝.每个宝贝贴着价值标签. 地宫的入口在左上角,出口在右下角. 小明被带到地宫的入口,国王要求他只能向右或向下行走. 走 ...

  6. 算法笔记_174:历届试题 地宫取宝(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 X 国王有一个地宫宝库.是 n x m 个格子的矩阵.每个格子放一件宝贝.每个宝贝贴着价值标签. 地宫的入口在左上角,出口在右下角. 小明 ...

  7. 蓝桥杯 地宫寻宝 带缓存的DFS

      历届试题 地宫取宝   时间限制:1.0s   内存限制:256.0MB      问题描写叙述 X 国王有一个地宫宝库. 是 n x m 个格子的矩阵. 每一个格子放一件宝贝. 每一个宝贝贴着价 ...

  8. Java实现 蓝桥杯 历届试题 地宫取宝

    问题描述 X 国王有一个地宫宝库.是 n x m 个格子的矩阵.每个格子放一件宝贝.每个宝贝贴着价值标签. 地宫的入口在左上角,出口在右下角. 小明被带到地宫的入口,国王要求他只能向右或向下行走. 走 ...

  9. 第五届蓝桥杯C++B组 地宫取宝

    代码: #include <bits/stdc++.h> using namespace std; #define ll long long const ll mod = 1e9 + 7; ...

随机推荐

  1. 转:在ElasticSearch之下(图解搜索的故事)

    ElasticSearch 2 (9) - 在ElasticSearch之下(图解搜索的故事) 摘要 先自上而下,后自底向上的介绍ElasticSearch的底层工作原理,试图回答以下问题: 为什么我 ...

  2. 配置Windows Server2008+iis+php+mysql所需下载安装包

    最近一个朋友让我帮忙给配置服务器iis+php+mysq 环境,遇到了很多问题,特此就在这里说一下.小弟只是在windwos2003 和windwos XP下配置过iis+php+mysql,去朋友那 ...

  3. 【linux】/etc/fstab修复

    /etc/fstab在修改后,如果配置错误直接重启的话会导致系统崩溃.建议大家重启前执行mount -a ,mount -a是自动挂载 /etc/fstab 里面的东西.若不慎重启了,会出现开机错误, ...

  4. WPF--Calendar控件高级使用

    一.得到当前显示的月份: DateTime SelectedDay = this.MC.DisplayDate; 二.得到当前选中的天,得到当前选中的周,得到当前显示的月份: 如果你使用系统默认的事件 ...

  5. Discuz!NT 3.9.913 Beta DIY过程

    前提: 论坛的源码版本为dnt_3.9.913_sqlserver_beta.zip,以下例子都以这个版本为原型修改 dnt_3.9.913数据字典:下载 目前(2013年10月21日)官网的asp. ...

  6. Func系列4:其他功能

    非Python API接口支持 Func通过非Python API实现远程调用,目的是为第三方工具提供调用及返回接口.Func使用func-transmit命令来实现,支持YAML.JSON格式,实现 ...

  7. C语言每日一题之No.3

    几天下来,感慨学习要坚持下来真的是件很难的事,本来说了每天一题,可是毕竟这是个细活,需要用心雕琢,有时候真的不能当天拿下来>_<.虽然说只是一题,却涉及到很多小细节,慢慢的琢磨直至完全摸透 ...

  8. gcc/g++ 静态动态库 混链接.

    我的环境: centos6 x64. gcc4.4.7 在使用gcc/g++ 编译程序时我们希望指向一些库是使用静态的链接方式. 另外的一些是动态的方式. 我以boost 为例. 如果我们要使用静态库 ...

  9. 黄聪:利用OpenXml生成Word2007文档(转)

    原文:http://blog.csdn.net/francislaw/article/details/7568317 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[-] 一Op ...

  10. PL/SQL查询Oracle数据乱码/Oracle客户端乱码解决办法

    [如果此方法都试了就是不行,那么就重复尝试,先把环境变量给删了,注册表里的键值也删除了,然后重启,再配置,肯定行!我试过!] 先确定Oracle服务器采用的是何种编码: select userenv( ...