【BZOJ3205_洛谷3638】[APIO2013]机器人(动态规划)
题目:
分析:
卡了一天的神题……(OrzJumpmelon)
首先预处理出从点\(p\)向\(d\)方向出发最终能到达的点\(nxt[p][d]\)。这个可以直接记忆化搜索解决。如果出现环说明不能向这个方向出发,设为\(-1\)。
struct point
{
int x, y;
point(const int _x = 0, const int _y = 0)
: x(_x), y(_y) {}
};
inline bool check(const point &p)
{
return p.x >= 0 && p.x < h && p.y >= 0 && p.y < w;
}
inline int ptoi(const point &p)
{
return p.x * w + p.y;
}
bool vis[P][DIR], insta[P][DIR];
int dfs(const point &u, const int &d)
{
int id = ptoi(u);
if (vis[id][d])
return nxt[id][d];
if (insta[id][d])
{
vis[id][d] = true;
return nxt[id][d] = -1;
}
int dd = d;
if (map[id] == LEFT)
dd = (dd + 1) & 3;
if (map[id] == RIGHT)
dd = (dd + 3) & 3;
point v = point(u.x + dx[dd], u.y + dy[dd]);
if (!check(v) || map[ptoi(v)] == WALL)
{
vis[id][d] = true;
return nxt[id][d] = id;
}
else
{
insta[id][d] = true;
nxt[id][d] = dfs(v, dd);
insta[id][d] = false;
vis[id][d] = true;
return nxt[id][d];
}
}
然后考虑用\(dp[i][j][u]\)表示令编号为\([i,j]\)的复合机器人在\(u\)点的最少步数,最终答案就是\(min(dp[0][n-1][u])\)。有两种转移方式:
1.把\([i,k]\)和\((k,j]\)两个机器人在\(p\)点拼起来,即:
\]
2.把\([i,j]\)机器人推到\(u\)点,即(其中\(v\)能一步走到\(u\)即存在满足\(nxt[v][d]=u\)的\(d\)):
\]
第一种直接区间DP即可。第二种存在循环更新,但是长得很像最短路……
于是我码了Dijkstra,卡常卡到死也没卡过去,还借此机会跟Jumpmelon谝了一天qwq。
下面介绍一下Jumpmelon给我讲的优化:开两个队列,第一个队列是一开始的点,按照\(dis\)排好序(\(dis[u]\)表示\(dp[i][j][u]\),下同);第二个队列是已经更新,等待用来更新别的点的队列,初始为空。每次将两个队列队首中\(dis\)较小的一个取出来(相当于Dijkstra的堆顶)来松弛其他点,这样复杂度是\(O(n+m)\)的,成功去掉堆的\(\log m\)。为什么这样是对的呢?
注意到所有边权都是\(1\),于是点\(v\)被更新时第二个队列的队尾(如果存在)的\(dis\)一定不会大于\(dis[v]\),所以直接把\(v\)插到队尾不会影响第二个队列的有序性。原因如下。
考虑反证。假设之前第二个队列是有序的,在某一次更新后变得无序了。设用于更新的点是\(u\),被更新的点是\(v\),更新前第二个队列的队尾为\(t\),满足\(dis[v]=dis[u]+1\),\(dis[t]>dis[v]\)。由于边权都是\(1\),那么曾用于更新\(t\)的点\(p\)满足\(dis[p]=dis[t]-1\geq dis[v]>dis[u]\)。既然\(dis[u]<dis[p]\),由于之前两个队列都是有序的,且每次是取出两队列队首的较小值更新,那么\(u\)显然应该在\(p\)之前被取出,矛盾。所以这种情况不存在。既然能保持两个队列的有序性,那么就保证了每次取出\(dis\)最小的点,也就不需要堆了。(没上文化课,表达能力为\(0\),自己都觉得佶屈聱牙跟个T嘴z子z一样。感性理解就好)。
代码:
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
#include <queue>
#include <functional>
#include <vector>
#undef i
#undef j
#undef k
#undef true
#undef false
#undef min
#undef max
#undef sort
#undef swap
#undef if
#undef while
#undef for
#undef printf
#undef scanf
#undef putchar
#undef getchar
#define _ 0
using namespace std;
namespace zyt
{
template<typename T>
inline bool read(T &x)
{
char c;
bool f = false;
x = 0;
do
c = getchar();
while (c != EOF && c != '-' && !isdigit(c));
if (c == EOF)
return false;
if (c == '-')
f = true, c = getchar();
do
x = x * 10 + c - '0', c = getchar();
while (isdigit(c));
if (f)
x = -x;
return true;
}
inline bool read(char &c)
{
do
c = getchar();
while (c != EOF && !isgraph(c));
return c != EOF;
}
template<typename T>
inline void write(T x)
{
static char buf[20];
char *pos = buf;
if (x < 0)
putchar('-'), x = -x;
do
*pos++ = x % 10 + '0';
while (x /= 10);
while (pos > buf)
putchar(*--pos);
}
const int W = 5e2, P = W * W, N = 9, DIR = 4, INF = 0x3f3f3f3f, B = 18;
const int U = 0, L = 1, D = 2, R = 3, WALL = N, LEFT = N + 1, RIGHT = N + 2, BLANK = N + 3;
const int dx[DIR] = {-1, 0, 1, 0};
const int dy[DIR] = {0, -1, 0, 1};
int n, w, h, p, nxt[P][DIR], map[P], dp[N][N][P];
struct point
{
int x, y;
point(const int _x = 0, const int _y = 0)
: x(_x), y(_y) {}
};
inline bool check(const point &p)
{
return p.x >= 0 && p.x < h && p.y >= 0 && p.y < w;
}
inline int ptoi(const point &p)
{
return p.x * w + p.y;
}
bool vis[P][DIR], insta[P][DIR];
int dfs(const point &u, const int &d)
{
int id = ptoi(u);
if (vis[id][d])
return nxt[id][d];
if (insta[id][d])
{
vis[id][d] = true;
return nxt[id][d] = -1;
}
int dd = d;
if (map[id] == LEFT)
dd = (dd + 1) & 3;
if (map[id] == RIGHT)
dd = (dd + 3) & 3;
point v = point(u.x + dx[dd], u.y + dy[dd]);
if (!check(v) || map[ptoi(v)] == WALL)
{
vis[id][d] = true;
return nxt[id][d] = id;
}
else
{
insta[id][d] = true;
nxt[id][d] = dfs(v, dd);
insta[id][d] = false;
vis[id][d] = true;
return nxt[id][d];
}
}
void Shortest_Path(const int l, const int r)
{
typedef pair<int, int> pii;
static pii tmp[P];
static bool vis[P];
static queue<int> q1, q2;
while (!q1.empty())
q1.pop();
while (!q2.empty())
q2.pop();
memset(vis, 0, sizeof(bool[p]));
int *dis = dp[l][r], cnt = 0;
for (int i = 0; i < p; i++)
if (map[i] != WALL && dis[i] < INF)
tmp[cnt++] = make_pair(dis[i], i), vis[i] = true;
sort(tmp, tmp + cnt);
for (int i = 0; i < cnt; i++)
q1.push(tmp[i].second);
while (!q1.empty() || !q2.empty())
{
int u = ((q1.empty() || (!q2.empty() && dis[q1.front()] > dis[q2.front()])) ? q2.front() : q1.front());
if (!q1.empty() && u == q1.front())
q1.pop();
else
q2.pop();
vis[u] = false;
for (int i = 0; i < DIR; i++)
{
int v = nxt[u][i];
if (~v && dis[v] > dis[u] + 1)
{
dis[v] = dis[u] + 1;
if (!vis[v])
q2.push(v), vis[v] = true;
}
}
}
}
int work()
{
read(n), read(w), read(h);
p = w * h;
for (int i = 0; i < n; i++)
for (int j = i; j < n; j++)
memset(dp[i][j], INF, sizeof(int[p]));
for (int i = 0; i < p; i++)
{
char c;
read(c);
if (isdigit(c))
{
map[i] = c - '1';
dp[map[i]][map[i]][i] = 0;
}
else if (c == '.')
map[i] = BLANK;
else if (c == 'x')
map[i] = WALL;
else if (c == 'A')
map[i] = LEFT;
else
map[i] = RIGHT;
}
for (int i = 0; i < h; i++)
for (int j = 0; j < w; j++)
{
int id = ptoi(point(i, j));
if (map[id] != WALL)
for (int d = 0; d < DIR; d++)
nxt[id][d] = dfs(point(i, j), d);
}
for (int l = 1; l <= n; l++)
for (int i = 0; i + l - 1 < n; i++)
{
int j = i + l - 1;
for (int u = 0; u < p; u++)
if (map[u] != WALL)
for (int k = i; k < j; k++)
dp[i][j][u] = min(dp[i][j][u], dp[i][k][u] + dp[k + 1][j][u]);
Shortest_Path(i, j);
}
int ans = INF;
for (int i = 0; i < p; i++)
ans = min(ans, dp[0][n - 1][i]);
if (ans == INF)
write(-1);
else
write(ans);
return (0^_^0);
}
}
int main()
{
return zyt::work();
}
【BZOJ3205_洛谷3638】[APIO2013]机器人(动态规划)的更多相关文章
- 洛谷 P4012 深海机器人问题【费用流】
题目链接:https://www.luogu.org/problemnew/show/P4012 洛谷 P4012 深海机器人问题 输入输出样例 输入样例#1: 1 1 2 2 1 2 3 4 5 6 ...
- 洛谷 P5469 - [NOI2019] 机器人(区间 dp+拉格朗日插值)
洛谷题面传送门 神仙题,放在 D1T2 可能略难了一点( 首先显然对于 P 型机器人而言,将它放在 \(i\) 之后它会走到左边第一个严格 \(>a_i\) 的位置,对于 Q 型机器人而言,将它 ...
- 洛谷P1280 && caioj 1085 动态规划入门(非常规DP9:尼克的任务)
这道题我一直按照往常的思路想 f[i]为前i个任务的最大空暇时间 然后想不出来怎么做-- 后来看了题解 发现这里设的状态是时间,不是任务 自己思维还是太局限了,题做得太少. 很多网上题解都反着做,那么 ...
- 【洛谷P1126】机器人搬重物
题目大意:给定一个 N 行,M 列的地图,一个直径为 1.6 的圆形机器人需要从起点走到终点,每秒钟可以实现:向左转,向右转,向前 1-3 步.求从起点到终点最少要多长时间. 题解:相比于普通的走迷宫 ...
- 洛谷P4012 深海机器人问题(费用流)
传送门 图给的好坑……还得倒过来…… 用大佬的图做个示范 我们考虑左图吧 把每一个点向下连边,容量$1$,费用为给出的价值(表示一个机器人可以过去取得标本) 再连一条边,容量$inf$,费用$0$(表 ...
- 洛谷P4012 深海机器人问题(费用流)
题目描述 深海资源考察探险队的潜艇将到达深海的海底进行科学考察. 潜艇内有多个深海机器人.潜艇到达深海海底后,深海机器人将离开潜艇向预定目标移动. 深海机器人在移动中还必须沿途采集海底生物标本.沿途生 ...
- Solution -「NOI 2021」「洛谷 P7740」机器人游戏
\(\mathcal{Description}\) Link. 自己去读题面叭~ \(\mathcal{Solution}\) 首先,参悟[样例解释 #2].一种暴力的思路即为钦定集合 \ ...
- 洛谷P3639 [APIO2013] 道路费用 [生成树的特殊算法]
题目传送门 道路费用 格式难调,题面就不放了. 分析: 这是一道要细(yan)心(jing)的生成树的好(gui)题. 首先我们看到$k$的范围非常小,那么我们就可以直接$2^k$枚举每一条加边是否选 ...
- 洛谷 P1464 Function【动态规划(递推)/记忆化搜索(递归)】
题目描述 对于一个递归函数w(a,b,c) 如果a<=0 or b<=0 or c<=0就返回值1. 如果a>20 or b>20 or c>20就返回w(20,2 ...
随机推荐
- LINUX驱动、系统底层
就业模拟测试题-LINUX驱动.系统底层工程师职位 本试卷从考试酷examcoo网站导出,文件格式为mht,请用WORD/WPS打开,并另存为doc/docx格式后再使用 试卷编号:143921试卷录 ...
- 【Codeforces 584C】Marina and Vasya
[链接] 我是链接,点我呀:) [题意] 题意 [题解] 设cnt表示s1和s2不同的字符的个数 如果cnt>2t 因为这cnt个位置肯定至少有一边不同 显然肯定会有一个f(s,S)的值大于t的 ...
- Android NumberProgressBar:动态移动显示百分比进度的进度条
Android NumberProgressBar:动态移动显示百分比进度的进度条 NumberProgressBar是github上一个开源项目,其项目主页是:https://github.c ...
- WCF 配置文件中的MaxStringContentLength & MaxReceivedMessageSize
中午测试员在测试系统模块时发现无法通过WCF从服务器下载数据,检查配置文件后,建议开发人员修改站点的WEB.CONFIG文件,具体修改对比如下: 旧的: <binding name=" ...
- postgresql备份和恢复
备份: pg_dump -d m3vg -h localhost -p 5432 -U delta -W -f 1024.dump -F tar 恢复: pg_restore -h localhost ...
- C++学习之动态数组类的封装
动态数组(Dynamic Array)是指动态分配的.可以根据需求动态增长占用内存的数组.为了实现一个动态数组类的封装,我们需要考虑几个问题:new/delete的使用.内存分配策略.类的四大函数(构 ...
- Python科学计算的瑞士军刀——Anaconda 安装与配置
Introduce Python是一种强大的编程语言.其提供了非常多用于科学计算的模块,常见的包含numpy.scipy和matplotlib.要利用Python进行科学计算.就须要一一安装所需的模块 ...
- Codeforces Round #272 (Div. 2) D.Dreamoon and Sets 找规律
D. Dreamoon and Sets Dreamoon likes to play with sets, integers and . is defined as the largest p ...
- Android 自带Base64加密解密
Android项目引用不到以下两个java类 import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; Android有自己的base ...
- What is a good buffer size for socket programming?
http://stackoverflow.com/questions/2811006/what-is-a-good-buffer-size-for-socket-programming 问题: We ...