UVALive 2147 Push!!(队列实现DP)
就我的理解来说这个题,本质上是一个DP题,不应该说是搜索,因为我的做法是把表格中所有的数据都找到,使用队列暴力来遍历出所有状态,因为题目中的数据范围小,所有耗时也小。
首先分析箱子是一个被动物体,人是主动物体,箱子的移动取决于人的移动,所以在bfs中只需要让人去移动,进而带动箱子就可以了。我们使用dp[x1][y1][x2][y2]来记录状态,分别代表人和箱子的位置。在队列实现DP的过程中,我们必须要把当前所在的情况标记为未走过,这个很重要。有人可能会质疑,这样可能导致无限的入队列,导致死循环,所以需要一个技巧,首先新的状态是否更新跟这个状态是否被标记无关,所以能否让这个点入队列,需要首先满足状态值得更新的条件,所以我们不可能去走无意义的回头路,不可能会产生走过来走过去,从而导致死循环的情况。然后就要满足这个点未被标记的情况,才能入队列,标记这个点。
当然有人也会问,只要把状态更新了就可以了,干嘛非要入队列呢?如果不仅队列,更新的仅仅是这个状态,其他的状态无法经过他转移,无法获得最优子结构,就不符合DP的思想,就是错误的了。(当然这个地方可能也仅仅是我不懂,但是这个标记方法还是很重要的),我给出一个例子吧。
0 0 0 0 0 0
0 0 1 1 1 0
0 0 1 4 0 0
0 0 1 2 1 0
0 0 0 0 3 0
0 0 0 0 1 0
0 0 0 0 0 0
我相信你很快就能找出两种方法来,往上走耗费的步数多,在bfs过程中也是后来达到的点,所以bfs第一次到两个加粗的0的时候,耗费步数少,但是推的次数多,而题目中要求最小的推数,与走的步数无关,所以这点必须更新并且入队列更新其他的点,如果我们没有把以前的标记消除,导致入队列失败,更新失败,答案出错。
最后一句话总结,在队列实现dp的过程中,一定记着把以前的标记消除,让新的点入队列,更新其他的状态,才能得到最后的最优解。
代码如下:
#include<iostream>
#include<cstdio>
#include<queue>
#include<cmath>
#include<cstring>
using namespace std;
#define maxn 10
#define INF 999999999
int dp[maxn][maxn][maxn][maxn],n,m,maps[maxn][maxn];
int go[][] = {{,},{-,},{,},{,-}};
int vis[maxn][maxn][maxn][maxn],endx,endy;
struct Node
{
int x1,y1,x2,y2;
Node () {};
Node(int xx1,int yy1,int xx2,int yy2)
{
x1 = xx1;
y1 = yy1;
x2 = xx2;
y2 = yy2;
}
};
void mark(Node a)
{
vis[a.x1][a.y1][a.x2][a.y2] = ;
}
bool ok(int x,int y)
{
return (x>=&&x<n && y>=&&y<m && maps[x][y]!=);
}
int bfs(Node start)
{
queue<Node> que;
memset(vis,,sizeof(vis));
while(!que.empty()) que.pop();
que.push(start);
dp[start.x1][start.y1][start.x2][start.y2] = ;
//mark(start);
while(!que.empty())
{
Node now = que.front();
que.pop();
int x1 = now.x1,y1 = now.y1;
int x2 = now.x2,y2 = now.y2;
vis[x1][y1][x2][y2] = ;///进入队列,消除标记
int xx1,xx2,yy1,yy2;
for(int i = ; i < ; i++)
{
xx1 = x1 + go[i][];
yy1 = y1 + go[i][];
if(ok(xx1,yy1))
{
if(xx1 == x2 && yy1 == y2)
{
xx2 = x2 + go[i][];
yy2 = y2 + go[i][];
if(ok(xx2,yy2))
{
if(dp[xx1][yy1][xx2][yy2]==- || dp[xx1][yy1][xx2][yy2]>dp[x1][y1][x2][y2]+)///第一层检验
{
dp[xx1][yy1][xx2][yy2] = dp[x1][y1][x2][y2]+;///更新当前点状态
if(!vis[xx1][yy1][xx2][yy2])
{
Node nxt(xx1,yy1,xx2,yy2);
mark(nxt);///进入队列,更新其他点
que.push(nxt);
}
} }
}
else
{
if(dp[xx1][yy1][x2][y2]== - || dp[xx1][yy1][x2][y2]>dp[x1][y1][x2][y2])
{
dp[xx1][yy1][x2][y2] = dp[x1][y1][x2][y2];
if(!vis[xx1][yy1][x2][y2])
{
Node nxt(xx1,yy1,x2,y2);
mark(nxt);
que.push(nxt);
}
}
}
}
}
}
int ans = INF;
for(int i = ; i < n; i++)
{
for(int j = ; j < m; j++)
{
//printf("dp[%d][%d] = %d\n",i,j,dp[i][j][endx][endy]);
if(dp[i][j][endx][endy] != -)
ans = min(ans,dp[i][j][endx][endy]);
}
}
if(ans != INF) return ans;
return -;
}
int main()
{
while(~scanf("%d%d",&m,&n))
{
if(n+m == ) break;
Node start;
for(int i = ; i < n; i++)
{
for(int j = ; j < m; j++)
{
scanf("%d",&maps[i][j]);
if(maps[i][j] == )
{
start.x2 = i;
start.y2 = j;
}
if(maps[i][j] == )
{
start.x1 = i;
start.y1 = j;
}
if(maps[i][j] == )
{
endx = i;
endy = j;
}
}
}
memset(dp,-,sizeof(dp));
int ans = bfs(start);
printf("%d\n",ans);
}
return ;
}
UVALive 2147 Push!!(队列实现DP)的更多相关文章
- hdu3401:单调队列优化dp
第一个单调队列优化dp 写了半天,最后初始化搞错了还一直wa.. 题目大意: 炒股,总共 t 天,每天可以买入na[i]股,卖出nb[i]股,价钱分别为pa[i]和pb[i],最大同时拥有p股 且一次 ...
- SCOI 股票交易 单调队列优化dp
这道题 我很蒙.....首先依照搞单调队列优化dp的一般思路 先写出状态转移方程 在想法子去优化 这个题目中说道w就是这一天要是进行操作就是从前w-1天转移而来因为之前的w天不允许有操作!就是与这些天 ...
- 「学习笔记」单调队列优化dp
目录 算法 例题 最大子段和 题意 思路 代码 修剪草坪 题意 思路 代码 瑰丽华尔兹 题意 思路 代码 股票交易 题意 思路 代码 算法 使用单调队列优化dp 废话 对与一些dp的转移方程,我们可以 ...
- 【转】单调队列优化DP
转自 : http://www.cnblogs.com/ka200812/archive/2012/07/11/2585950.html 单调队列是一种严格单调的队列,可以单调递增,也可以单调递减.队 ...
- 单调队列优化DP,多重背包
单调队列优化DP:http://www.cnblogs.com/ka200812/archive/2012/07/11/2585950.html 单调队列优化多重背包:http://blog.csdn ...
- bzoj1855: [Scoi2010]股票交易--单调队列优化DP
单调队列优化DP的模板题 不难列出DP方程: 对于买入的情况 由于dp[i][j]=max{dp[i-w-1][k]+k*Ap[i]-j*Ap[i]} AP[i]*j是固定的,在队列中维护dp[i-w ...
- 【HDU 3401 Trade】 单调队列优化dp
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3401 题目大意:现在要你去炒股,给你每天的开盘价值,每股买入价值为ap,卖出价值为bp,每天最多买as ...
- Parade(单调队列优化dp)
题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=2490 Parade Time Limit: 4000/2000 MS (Java/Others) ...
- BZOJ_3831_[Poi2014]Little Bird_单调队列优化DP
BZOJ_3831_[Poi2014]Little Bird_单调队列优化DP Description 有一排n棵树,第i棵树的高度是Di. MHY要从第一棵树到第n棵树去找他的妹子玩. 如果MHY在 ...
随机推荐
- 如何在Excel中少犯二(I)
作者:何明科链接:https://zhuanlan.zhihu.com/p/23472480来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 收到不少建议,要求开知乎Li ...
- OAuth流程
简介 OAuth是一种协议,OAuth协议为用户资源的授权提供了一个安全的.开放而又简易的标准 第三方若想访问用户资源,就必须遵守服务提供商实现的OAuth协议 OAuth授权的步骤(新浪微博为例) ...
- 2.编写IoDemo.java的Java应用程序,程序完成的功能是:首先读取text.txt文件内容,再通过键盘输入文件的名称为iodemo.txt,把text.txt的内容存入iodemo.txt
package zuoye; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; ...
- User already has more than 'max_user_connections' active connections
vi /etc/my.cnf修改 max_user_connections 参数的数值,重启 MySQL 服务器
- Xcode最最实用快捷键
转发:http://www.open-open.com/lib/view/open1397988745593.html 关于与mac 通用的快捷键网上太多,就不说了,下面介绍一些大家不是很熟悉但是 ...
- JavaScript Function arguments.callee caller length return
一.Function 函数是对象,函数名是指针. 函数名实际上是一个指向函数对象的指针. 使用不带圆括号的函数名是访问函数指针,并非调用函数. 函数的名字仅仅是一个包含指针的变量而已.即使在不同的环境 ...
- 1001. A+B Format
Calculate a + b and output the sum in standard format -- that is, the digits must be separated into ...
- 转:jmeter之线程组
虽然有三个添加线程组的选项,名字不一样, 创建之后,其界面是完全一样的.之前的版本只有一个线程组的名字.现在多一个setUp theread Group 与terDown Thread Group 1 ...
- 【转载】将绿色版Tomcat服务添加到系统服务并设为开机运行
转自: http://www.cnblogs.com/lan0725/archive/2009/11/04/1873859.html 运行cmd打开控制台,进入Tomat目录/bin文件夹,输入如 ...
- STM32 一通道单次转换
之前弄过ADC连续转换,还配了DMA传输,项目上不希望这样做,因为有其他设备用到DMA传输,就会不停的抢占优先级,效率低. 按照需求改成ADC单次转换,非连续,用的时候只需调下函数执行一次转换即可. ...