记录编号 105718 评测结果 AAAAAAAAAA
题目名称 [POJ 2841] 航海游戏 最终得分 100
用户昵称 Gravatarcstdio 是否通过 通过
代码语言 C++ 运行时间 2.671 s
提交时间 2014-06-12 10:20:01 内存使用 5.47 MiB
显示代码纯文本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<deque>
using namespace std;
const int SIZEN=1010,INF=0x7fffffff/2;
int N,M;//N行M列
char board[SIZEN][SIZEN]={0};
int f[SIZEN][2][SIZEN]={0};
int T[2][SIZEN]={0},a[SIZEN]={0};
char line[SIZEN];
int sa[SIZEN]={0},sp[SIZEN]={0};
int wheel[SIZEN]={0};
void reverse(int *s){//翻转s[1]~s[M]
	int i=1,j=M;
	while(i<j){
		swap(s[i],s[j]);
		i++,j--;
	}
}
void reverse(char *s){//翻转s[1]~s[M]
	int i=1,j=M;
	while(i<j){
		swap(s[i],s[j]);
		i++,j--;
	}
}
int cost(int k,bool flag,int j){
	return T[flag][k]+((j-k+1)*(j-k))/2+sp[j]-sp[k]-k*(sa[j]-sa[k]);
}
class POINT{
public:
	int x,y;//x其实就是下标
	bool flag;//命运之轮次数奇偶性
	void print(void){printf("(%intd %intd %d)",x,y,flag);}
};
int calc_Y(int k,int flag){
	return k*k-2*sp[k]+2*k*sa[k]+2*T[flag][k];
}
bool check(POINT a,POINT b,int k){//ab斜率是否大于k,其中b的横坐标一定比a大
	return (b.y-a.y)>k*(b.x-a.x);
}
class CONQUEUE{//斜率优化维护凸包,凸包是下凸的
public:
	deque<POINT> Q;
	void push(int x,int flag,int k){//将下标为x纳入考虑,并用斜率k维护凸包
		int y=calc_Y(x,flag);
		POINT temp;
		temp.x=x,temp.flag=flag,temp.y=y;
		while(Q.size()&&!check(Q.back(),temp,k)) Q.pop_back();
		Q.push_back(temp);
	}
	void maintain_front(int k){
		while(Q.size()>=2&&!check(Q[0],Q[1],k)) Q.pop_front();
	}
	int frontcost(int j){//刚才已经维护过了,计算走到j的最优解
		if(Q.empty()) return -1;
		return cost(Q.front().x,Q.front().flag,j);
	}
	void clear(void){
		Q.clear();
	}
};
CONQUEUE Q[2];
void DP_line(int f[2][SIZEN]){//这个函数需要指定line,T,a
	Q[0].clear(),Q[1].clear();
	memset(f,-1,sizeof(int)*2*SIZEN);
	for(int j=1;j<=M;j++){
		sa[j]=sa[j-1]+a[j];
		sp[j]=sp[j-1]+a[j]*j;
		wheel[j]=wheel[j-1]+(line[j]=='F');
		if(line[j]=='O'){//这是一块石头
			Q[0].clear(),Q[1].clear();
			continue;
		}
		int k=2*j+1+2*sa[j];//斜率
		for(int t=0;t<=1;t++){
			bool flag=t^(wheel[j-1]&1);
			if(T[t][j]==-1) continue;
			Q[flag].push(j,t,k);//这里新加入决策
		}
		Q[0].maintain_front(k),Q[1].maintain_front(k);
		for(int t=0;t<=1;t++){
			bool flag=t^(wheel[j]&1);
			f[t][j]=Q[flag].frontcost(j);
		}
	}
}
void prepare_line(int i){
	memcpy(line,board[i],sizeof(line));
	memset(T[0],-1,sizeof(T[0])),memset(T[1],-1,sizeof(T[1]));
	for(int t=0;t<=1;t++){
		for(int j=1;j<=M;j++){
			if(f[i+1][t][j]==-1) continue;
			if(line[j]=='O') continue;
			T[t][j]=f[i+1][t][j]+1;
			if(line[j]=='B') T[t][j]--;
			else if(line[j]=='S') T[t][j]++;
		}
	}
	for(int j=1;j<=M;j++){
		if(line[j]=='B') a[j]=-1;
		else if(line[j]=='S') a[j]=1;
		else a[j]=0;
	}
}
int g1[2][SIZEN]={0},g2[2][SIZEN]={0};
int DPmin(int a,int b){
	if(a==-1) return b;
	if(b==-1) return a;
	return min(a,b);
}
void DP(void){
	for(int i=1;i<=M;i++) if(board[N][i]=='H') f[N][0][i]=0;
	for(int i=N-1;i>=2;i--){
		prepare_line(i);
		DP_line(g1);
		reverse(line),reverse(T[0]),reverse(T[1]),reverse(a);
		DP_line(g2);
		for(int j=1;j<=M;j++){
			for(int t=0;t<=1;t++){
				f[i][t][j]=DPmin(g1[t][j],g2[t][M+1-j]);
			}
		}
	}
	int ans=INF;
	for(int i=1;i<=M;i++){
		if(board[1][i]!='H') continue;
		if(f[2][1][i]==-1) continue;
		ans=min(ans,f[2][1][i]+1);
	}
	if(ans==INF) printf("No solution\n");
	else printf("%d\n",ans);
}
void read(void){
	memset(f,-1,sizeof(f));
	scanf("%d%d",&N,&M);
	for(int i=1;i<=N;i++) scanf("%s",&board[i][1]);
}
int main(){
	freopen("navigationgame.in","r",stdin);
	freopen("navigationgame.out","w",stdout);
	read();
	DP();
	return 0;
}