洛谷上这道题的第一篇题解。上海加油。
题目大意
Aladdin 已经厌倦了宫殿里的生活。他有一份稳定的工作,他的妻子 Jasmine 和孩子们都在路上,生活变得单调。在这一切的驱使下,他决定在安顿下来之前再进行一次冒险。他决定找到 Golden Pear,这是一件极为珍贵的传奇文物,至今无人能找到。
Aladdin 正在寻找的沙漠可以被看作是一个 n×nn×nn×n 的网格。行和列从上到下、从左到右编号为 111 到 NNN。沙漠中的网格里一共有 MMM 个巫师,他们以一种不同寻常的方式帮助 Aladdin 完成任务。
Aladdin 星期一在沙漠的左上角 (1,1)(1,1)(1,1) 开始他的任务并面朝右。他的动作包括重复这些步骤:
- 如果当前的网格里有一个醒着的巫师,那么 Aladdin 会根据巫师说的话向左或向右转 909090 度。
- 如果向前走会把 Aladdin 带出沙漠,他会转过 180180180 度。
- Aladdin 向前移动了一格,这将花费他一天的时间。
对于每个巫师,我们都知道他的位置和一周中每一天的活动计划。每个巫师的日程表是一个由七个字母(仅包含 L、R 或 S)组成的字符串,每个字符告诉我们巫师在一周中的某一天(从星期一开始)做什么。字母 L 表示 Aladdin 将在这一天被告知向左转,R 表示 Aladdin 将在这一天被告知向右转,而 S 表示巫师那天正在睡觉。
一个古老的预言说,在改变 KKK 次方向(通过步骤 111 和步骤 222)后,Aladdin 会找到 Golden Pear。根据古老的预言,写一个程序来计算冒险将持续多少天。
大体思路
按照题意,定义一个状态为 (x,y,dir,day)(x,y,dir,day)(x,y,dir,day),表示当前位置 (x,y)(x,y)(x,y) 在星期 daydayday 被访问,方向为 dirdirdir,其中 x,y≤200x,y\le 200x,y≤200,dir={0,1,2,3}dir=\{0,1,2,3\}dir={0,1,2,3},day={0,1,2,3,4,5,6}day=\{0,1,2,3,4,5,6\}day={0,1,2,3,4,5,6}。因此,总的状态数为 2002×4×7≈106200^2\times 4\times 7≈10^62002×4×7≈106。所以,我们完全可以用数组直接存储每一个状态。
记 turns[x,y,dir,day]turns[x,y,dir,day]turns[x,y,dir,day] 表示上一次在星期 daydayday 以 dirdirdir 方向来到 (x,y)(x,y)(x,y) 时转向的次数,而 date[x,y,dir,day]date[x,y,dir,day]date[x,y,dir,day] 则表示上一次在星期 daydayday 以 dirdirdir 方向来到 (x,y)(x,y)(x,y) 时的天数。设当前转向次数为 kkk,天数为 ansansans,那么,从上一次访问到这一次访问的循环,转向的次数为 Δk=k−turns[x,y,dir,day]\Delta k=k-turns[x,y,dir,day]Δk=k−turns[x,y,dir,day],循环的天数为 Δt=ans−date[x,y,dir,day]\Delta t=ans - date[x,y,dir,day]Δt=ans−date[x,y,dir,day]。
由于距离目标 KKK 次转向还有 K−kK-kK−k 次,可以完成的完整的循环次数为 times=⌊K−kΔk⌋times=\left\lfloor\dfrac{K-k}{\Delta k}\right\rfloortimes=⌊ΔkK−k⌋。那么,可以直接将转向次数增加 Δk×times\Delta k\times timesΔk×times,天数增加 Δt×times\Delta t \times timesΔt×times。然后,在对 date,turnsdate,turnsdate,turns 用目前状态的 ans,kans,kans,k 进行更新即可。
具体实现时,还需要在永久循环的开头判断 L,R 以及边界的转向,并调整 dir,kdir,kdir,k 等。
完整代码
#include <bits/stdc++.h>
using namespace std;
#define rep(ii,aa,bb) for(re int ii = aa; ii <= bb; ii++)
#define Rep(ii,aa,bb) for(re int ii = aa; ii >= bb; ii--)
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef pair<int, int> PII;
const int maxn = 205;
namespace IO_ReadWrite {
#define re register
#define gg (p1 == p2 && (p2 = (p1 = _buf) + fread(_buf, 1, 1<<21, stdin), p1 == p2) ? EOF :*p1++)
char _buf[1<<21], *p1 = _buf, *p2 = _buf;
template <typename T>
inline void read(T &x){
x = 0; re T f=1; re char c = gg;
while(c > 57 || c < 48){if(c == '-') f = -1;c = gg;}
while(c >= 48 &&c <= 57){x = (x<<1) + (x<<3) + (c^48);c = gg;}
x *= f;return;
}
inline void ReadChar(char &c){
c = gg;
while(!isalpha(c)) c = gg;
}
template <typename T>
inline void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x/10);
putchar('0' + x % 10);
}
template <typename T>
inline void writeln(T x){write(x); putchar('\n');}
}
using namespace IO_ReadWrite;
int dx[4] = {0, 1, 0, -1},
dy[4] = {1, 0, -1, 0};//right, down, left, up
int N, M, K, turns[maxn][maxn][4][8];
char mp[maxn][maxn][8];
ll date[maxn][maxn][4][8];
int main () {
scanf("%d%d%d", &N, &K, &M);
rep(i, 1, M) {
int x, y;
scanf("%d%d", &x, &y);
scanf("%s", mp[x][y]);
}
ll ans = 0;
int x = 1, y = 1, dir = 0, day = 0, k = 0;
while(1) {
if(mp[x][y][day] == 'L') dir = (dir + 3) % 4, k ++;
if(mp[x][y][day] == 'R') dir = (dir + 1) % 4, k ++;
int nx = x + dx[dir], ny = y + dy[dir];
if(nx < 1 || nx > N || ny < 1 || ny > N) dir = (dir + 2) % 4, k ++;
if(date[x][y][dir][day]) {
int newK = k - turns[x][y][dir][day];
int left = K - k;
ans += (left / newK) * (ans - date[x][y][dir][day]);
k += (left / newK) * newK;
}
date[x][y][dir][day] = ans;
turns[x][y][dir][day] = k;
if(k >= K) break;
x = x + dx[dir], y = y + dy[dir];
ans ++;
if(++day >= 7) day = 0;
}
writeln(ans);
return 0;
}
本文详细解析了COCI2008-2009#5 KRUSKA问题,介绍了Aladdin在寻找Golden Pear的过程中,如何根据巫师的指示在沙漠网格中移动。文章阐述了大体思路,提出使用状态空间(位置、方向、日期)来解决问题,并通过动态规划存储每次状态转移的转向次数和日期,以求解达到预言中转向次数所需的最少天数。最后,给出了问题的具体代码实现。

453

被折叠的 条评论
为什么被折叠?



