YY_More

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

这道是NOI2005的原题吧

这道题乍一看,很容易设计出状态f[T][N][M],而且鉴于T有40000,K只有200,倾斜方向也是基于K的,因此把状态设计成f[K][N][M],即第K段时间滑到(N,M)位置的最大滑行距离。

设p[K]为第K段时间的持续长度,这样的话,根据倾斜方向不同,我们得到了四种转移方程。

f[K][N][M]=max(f[K-1][i][j]+dist)(dist<=p[K])

向上时:j=M,i>=N,dist=i-N

向下时:j=M,i<=N,dist=N-i

向左时:i=N,j>=M,dist=j-M

向右时:i=N,j<=M,dist=M-j

可以把K的那一维数组滚动掉,但是我很懒,没有这样做。

问题来了,这个转移方程虽然很有效,但是直接做会超时。我们发现,每次转移事实上都是在一行或者一列上进行取最大值的操作,这一操作不优化的话复杂度是O(M)或O(N),

把这一维单独拿出来(仅以向下滑动为例,其它的类比),就是f[N]=max(f[i]+N-i)=max(f[i]-i)+N(N-i<=p[K])

这样的话就符合了单调队列优化的基本模型,队列中存的是f[i]-i,当遇到障碍物时将队列清空。转移的复杂度降为O(1),最后的复杂度为O(KMN)

//By YY_More
#include<cstdio>
#include<iostream>
using namespace std;
char a[210][210];
int f[210][210][210];
int N,M,x,y,K,L,R,s,t,p[300],last[300],D[300];
void goup(int x){
	for (int j=1;j<=M;j++){
		L=0;R=-1;
		for (int i=N;i>0;i--)
			if (a[i][j]=='x') {L=0;R=-1;}
			else{
				while (L<=R&&f[x-1][D[R]][j]+D[R]<=f[x-1][i][j]+i) R--;
				D[++R]=i;
				while (D[L]-i>last[x]) L++;
				f[x][i][j]=f[x-1][D[L]][j]+D[L]-i;
		}
	}
};	
void godown(int x){
	for (int j=1;j<=M;j++){
		L=0;R=-1;
		for (int i=1;i<=N;i++)
			if (a[i][j]=='x') {L=0;R=-1;}
			else{
				while (L<=R&&f[x-1][D[R]][j]-D[R]<=f[x-1][i][j]-i) R--;
				D[++R]=i;
				while (i-D[L]>last[x]) L++;
				f[x][i][j]=f[x-1][D[L]][j]+i-D[L];
		}
	}
};	
void goleft(int x){
	for (int i=1;i<=N;i++){
		L=0;R=-1;
		for (int j=M;j>0;j--)
			if (a[i][j]=='x') {L=0;R=-1;}
			else{
				while (L<=R&&f[x-1][i][D[R]]+D[R]<=f[x-1][i][j]+j) R--;
				D[++R]=j;
				while (D[L]-j>last[x]) L++;
				f[x][i][j]=f[x-1][i][D[L]]+D[L]-j;
		}
	}
};	
void goright(int x){
	for (int i=1;i<=N;i++){
		L=0;R=-1;
		for (int j=1;j<=M;j++)
			if (a[i][j]=='x') {L=0;R=-1;}
			else{
				while(L<=R&&f[x-1][i][D[R]]-D[R]<=f[x-1][i][j]-j) R--;
				D[++R]=j;
				while (j-D[L]>last[x]) L++;
				f[x][i][j]=f[x-1][i][D[L]]+j-D[L];
		}
	}
};	
int main(){
	scanf("%d%d%d%d%d",&N,&M,&x,&y,&K);
	getchar();
	for (int i=1;i<=N;i++){
		for (int j=1;j<=M;j++)
		    a[i][j]=getchar();
		getchar();
	}
	for (int i=1;i<=K;i++){
		scanf("%d%d%d",&s,&t,&p[i]);
		last[i]=t-s+1;
	}	
	fill(&f[0][0][0],&f[K][N][M]+1,-50000);
	f[0][x][y]=0;
	for (int h=1;h<=K;h++)
		switch(p[h]){
			case 1:goup(h);break;
			case 2:godown(h);break;
			case 3:goleft(h);break;
			case 4:goright(h);break;
	}
	int ans=0;
	for (int i=1;i<=N;i++)
		for (int j=1;j<=M;j++)
			if (f[K][i][j]>ans) ans=f[K][i][j];
	cout<<ans<<endl;
	return 0;
}
posted on 2011-06-22 15:56  YY_More  阅读(522)  评论(0编辑  收藏  举报