1. 程式人生 > >(賽前練手#3)BZOJ1499[NOI2005] 瑰麗華爾茲(單調佇列優化DP)

(賽前練手#3)BZOJ1499[NOI2005] 瑰麗華爾茲(單調佇列優化DP)

1499: [NOI2005]瑰麗華爾茲

Time Limit: 3 Sec Memory Limit: 64 MB Submit: 2123 Solved: 1291 [Submit][Status][Discuss] Description

你跳過華爾茲嗎?當音樂響起,當你隨著旋律滑動舞步,是不是有一種漫步仙境的愜意?眾所周知,跳華爾茲時,最重要的是有好的音樂。但是很少有幾個人知道,世界上最偉大的鋼琴家一生都漂泊在大海上,他的名字叫丹尼•佈德曼•T.D.•檸檬•1900,朋友們都叫他1900。 1900在20世紀的第一年出生在往返於歐美的郵輪弗吉尼亞號上,很不幸他剛出生就被拋棄了,成了孤兒。1900孤獨的成長在弗吉尼亞號上,從未離開過這個搖晃的世界。也許是對他命運的補償,上帝派可愛的小天使艾米麗照顧他。可能是天使的點化,1900擁有不可思議的鋼琴天賦:從未有人教,從沒看過樂譜,但他卻能憑著自己的感覺彈出最沁人心脾的旋律。當1900的音樂獲得郵輪上所有人的歡迎時,他才8歲,而此時的他已經乘著海輪往返歐美大陸50餘次了。雖說是鋼琴奇才,但1900還是個孩子,他有著和一般男孩一樣的好奇和調皮,只不過更多一層浪漫的色彩罷了:這是一個風雨交加的夜晚,海風捲起層層巨浪拍打著弗吉尼亞號,郵輪隨著巨浪劇烈的搖擺。船上的新薩克斯手馬克斯•託尼暈船了,1900招呼託尼和他一起坐上舞廳裡的鋼琴,然後鬆開了固定鋼琴的閘,於是,鋼琴隨著海輪的傾斜滑動起來。準確的說,我們的主角1900、鋼琴、郵輪隨著1900的旋律一起跳起了華爾茲,隨著“嘣嚓嚓”的節奏,託尼的暈船症也奇蹟般的消失了。後來託尼在回憶錄上寫道:大海搖晃著我們使我們轉來轉去快速的掠過燈和傢俱我意識到我們正在和大海一起跳舞真是完美而瘋狂的舞者晚上在金色的地板上快樂的跳著華爾茲是不是很愜意呢?也許,我們忘記了一個人,那就是艾米麗,她可沒閒著:她必須在適當的時候施展魔法幫助1900,不讓鋼琴碰上舞廳裡的傢俱。不妨認為舞廳是一個N行M列的矩陣,矩陣中的某些方格上堆放了一些傢俱,其他的則是空地。鋼琴可以在空地上滑動,但不能撞上傢俱或滑出舞廳,否則會損壞鋼琴和傢俱,引來難纏的船長。每個時刻,鋼琴都會隨著船體傾斜的方向向相鄰的方格滑動一格,相鄰的方格可以是向東、向西、向南或向北的。而艾米麗可以選擇施魔法或不施魔法:如果不施魔法,則鋼琴會滑動;如果施魔法,則鋼琴會原地不動。艾米麗是個天使,她知道每段時間的船體的傾斜情況。她想使鋼琴在舞廳裡滑行路程儘量長,這樣1900會非常高興,同時也有利於治療託尼的暈船。但艾米麗還太小,不會算,所以希望你能幫助她。

Input

輸入檔案的第一行包含5個數N, M, x, y和K。N和M描述舞廳的大小,x和y為鋼琴的初始位置(x行y列);我們對船體傾斜情況是按時間的區間來描述的,且從1開始計量時間,比如“在[1, 3]時間裡向東傾斜,[4, 5]時間裡向北傾斜”,因此這裡的K表示區間的數目。以下N行,每行M個字元,描述舞廳裡的傢俱。第i行第j列的字元若為‘ . ’,則表示該位置是空地;若為‘ x ’,則表示有傢俱。以下K行,順序描述K個時間區間,格式為:si ti di。表示在時間區間[si, ti]內,船體都是向di方向傾斜的。di為1, 2, 3, 4中的一個,依次表示北、南、西、東(分別對應矩陣中的上、下、左、右)。輸入保證區間是連續的,即 s1 = 1 si = ti-1 + 1 (1 < i ≤ K) tK = T

Output

輸出檔案僅有1行,包含一個整數,表示鋼琴滑行的最長距離(即格子數)。

Sample Input

4 5 4 1 3

…xx.

…x.

1 3 4

4 5 1

6 7 3

Sample Output

6 HINT

Source

鳴謝劉汝佳先生授權使用

題面背景還是很給力的。。。 由於同一時間段方向是同樣的,因此我們不用去列舉時刻,只需要列舉時間段 設f[i][j][k] 為前i個時間段中從第(j , k)的位置出發所能獲得的MAX答案,因此我們有狀態轉移方程

若方向向右,則 f[i][x][y] = f[i - 1][j][y] + (x - j) (x - L <= j <= x) L為時間段長度

其餘同理

所以這就是一個滑動視窗(恆定長度區間RMQ)了,可以使用單調佇列 單調佇列建議手寫,陣列可以滾動(雖然此題兩種都不卡。。。)

AC Code:

#include<bits/stdc++.h>
#include <deque>
#define rg register
#define il inline
#define ll long long
#define maxn 200005
using namespace std;
il int read(){rg int x = 0 , w = 1;rg char ch = getchar();while (ch < '0' || ch > '9'){if (ch == '-') w = -1;ch = getchar();}while (ch >= '0' && ch <= '9'){x = (x << 3) + (x << 1) + ch - '0';ch = getchar();}return x * w;}
int f[2][202][202] , l[maxn] , r[maxn] , id[maxn] , n , m , k;
int ans;
bool mark[1002][1002];
char s[1002];
int dx[5] = {0 , -1 , 1 , 0 , 0} , dy[5] = {0 , 0 , 0 , -1 , 1};
struct node{
	int v , index;	
};
pair<int , int> q[maxn] , tmp;
void dp(int len , int x , int y , int k , int id){
	rg int l = 1 , r = 0;
	for (rg int i = 0 ; x >= 1 && x <= n && y >= 1 && y <= m ; ++i , x += dx[k] , y += dy[k]){
		if (mark[x][y]){
			l = 1 , r = 0;
			continue;		
		}
		tmp.first = f[!(id & 1)][x][y] , tmp.second = i;
		while (l <= r && tmp.first + (q[r].second - i) >= q[r].first) --r;
		q[++r] = tmp;
		while (l <= r && (i - q[l].second) > len) ++l;
		f[id & 1][x][y] = q[l].first + (i - q[l].second);
		ans = max(f[id & 1][x][y] , ans);
	}
}
int main(){
	n = read() , m = read();
	ans = 0;
	rg int x = read() , y = read();
	k = read();
    for (rg int i = 1 ; i <= n ; ++i){
    	scanf("%s" , s);
    	for (rg int j = 1 ; j <= m ; ++j)
    		if (s[j - 1] == 'x') mark[i][j] = 1;
    }
    memset(f , ~127 , sizeof(f));
    f[0][x][y] = 0;
    for (rg int i = 1 ; i <= k ; ++i)
    	l[i] = read() , r[i] = read() , id[i] = read();
    for (rg int i = 1 ; i <= k ; ++i){
    	rg int len = r[i] - l[i] + 1;
    	if (id[i] == 1)
    		for (rg int j = 1 ; j <= m ; ++j)
    			dp(len , n , j , 1 , i);
    	else if (id[i] == 2)
    		for (rg int j = 1 ; j <= m ; ++j)
				dp(len , 1 , j , 2 , i);
		else if (id[i] == 3)
			for (rg int j = 1 ; j <= n ; ++j)
				dp(len , j , m , 3 , i);
		else 
			for (rg int j = 1 ; j <= n ; ++j)
				dp(len , j , 1 , 4 , i);
	}
	cout << ans;
	return 0;	
}