1. 程式人生 > 其它 >P7683 [COCI2008-2009#5] KRUSKA

P7683 [COCI2008-2009#5] KRUSKA

洛谷上這道題的第一篇題解。上海加油。

題目大意

Aladdin 已經厭倦了宮殿裡的生活。他有一份穩定的工作,他的妻子 Jasmine 和孩子們都在路上,生活變得單調。在這一切的驅使下,他決定在安頓下來之前再進行一次冒險。他決定找到 Golden Pear,這是一件極為珍貴的傳奇文物,至今無人能找到。

Aladdin 正在尋找的沙漠可以被看作是一個 \(n×n\) 的網格。行和列從上到下、從左到右編號為 \(1\)\(N\)。沙漠中的網格里一共有 \(M\) 個巫師,他們以一種不同尋常的方式幫助 Aladdin 完成任務。

Aladdin 星期一在沙漠的左上角 \((1,1)\) 開始他的任務並面朝右。他的動作包括重複這些步驟:

  • 如果當前的網格里有一個醒著的巫師,那麼 Aladdin 會根據巫師說的話向左或向右轉 \(90\) 度。
  • 如果向前走會把 Aladdin 帶出沙漠,他會轉過 \(180\) 度。
  • Aladdin 向前移動了一格,這將花費他一天的時間。

對於每個巫師,我們都知道他的位置和一週中每一天的活動計劃。每個巫師的日程表是一個由七個字母(僅包含 LRS)組成的字串,每個字元告訴我們巫師在一週中的某一天(從星期一開始)做什麼。字母 L 表示 Aladdin 將在這一天被告知向左轉,R 表示 Aladdin 將在這一天被告知向右轉,而 S 表示巫師那天正在睡覺。

一個古老的預言說,在改變 \(K\)

次方向(通過步驟 \(1\) 和步驟 \(2\))後,Aladdin 會找到 Golden Pear。根據古老的預言,寫一個程式來計算冒險將持續多少天。

大體思路

按照題意,定義一個狀態為 \((x,y,dir,day)\),表示當前位置 \((x,y)\) 在星期 \(day\) 被訪問,方向為 \(dir\),其中 \(x,y\le 200\)\(dir=\{0,1,2,3\}\)\(day=\{0,1,2,3,4,5,6\}\)。因此,總的狀態數為 \(200^2\times 4\times 7≈10^6\)。所以,我們完全可以用陣列直接儲存每一個狀態。

\(turns[x,y,dir,day]\)

表示上一次在星期 \(day\)\(dir\) 方向來到 \((x,y)\) 時轉向的次數,而 \(date[x,y,dir,day]\) 則表示上一次在星期 \(day\)\(dir\) 方向來到 \((x,y)\) 時的天數。設當前轉向次數為 \(k\),天數為 \(ans\),那麼,從上一次訪問到這一次訪問的迴圈,轉向的次數為 \(\Delta k=k-turns[x,y,dir,day]\),迴圈的天數為 \(\Delta t=ans - date[x,y,dir,day]\)

由於距離目標 \(K\) 次轉向還有 \(K-k\) 次,可以完成的完整的迴圈次數為 \(times=\left\lfloor\dfrac{K-k}{\Delta k}\right\rfloor\)。那麼,可以直接將轉向次數增加 \(\Delta k\times times\),天數增加 \(\Delta t \times times\)。然後,在對 \(date,turns\) 用目前狀態的 \(ans,k\) 進行更新即可。

具體實現時,還需要在永久迴圈的開頭判斷 LR 以及邊界的轉向,並調整 \(dir,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;
}