本题的朴素算法很容易想出:枚举k,然后判断这个k所需要的调转次数m,并用m更新答案。但是,在求k的掉转次数m时,只要找到一个面向后面的牛,就要将它后面的k头牛再调转一遍,这样很容易浪费大量时间。也就是说,在最坏的情况下,复杂度趋近于O(n^3)!这是不可以的,必须考虑优化。

优化方法如下:

在读入时做一个巧妙的处理,设map数组表示这个位置上牛的状况,map[i]=0表示这头牛的方向与前一头相同,map[i]=1表示这头牛的方向与前一头牛不同。默认第0头牛的方向为向前(也就是F)。我们要做的就是将所有的牛的方向调转至和第一头牛相同。

那么,每一次对于牛i,调转从牛i开始的k头牛,是不会改变这k头牛的位置关系的。改变只有牛i与牛i-1的关系以及牛i+k与牛i+k-1的关系。所以,只需要将map[i]改为1-map[i],将map[i+k]改为1-map[i+k]即可踩掉花哥无压力~

有图为证:

POJ3276's Status~

代码如下:

Program Cowturn;//By_Thispoet
Const
	maxn=5001;
Var
	i,m,n,temp,ans,ansnum				:Longint;
	ch,c								:Char;
	map,remap							:Array[1..maxn]of Longint;

Function Calc(i:Longint):Longint;
var j:Longint;
begin
	for j:=1 to n do map[j]:=remap[j];
	Calc:=0;m:=1;
	while m<=n do
		begin
			while (map[m]=0)and(m<=n) do inc(m);
			if (m>n-i+1)and(m<=n)then exit(maxlongint);
			if m>n then exit;
			inc(Calc);
			map[i+m]:=1-map[i+m];
			inc(m);
		end;
end;


Procedure Printf();
begin
	writeln(ansnum,' ',ans);
end;


BEGIN

	readln(n);
	c:='F';
	for i:=1 to n do
		begin
			readln(ch);
			if ch=c then remap[i]:=0 else
				begin
					c:=ch;
					remap[i]:=1;
				end;
		end;

	ans:=maxlongint;
	for i:=1 to n do
		begin
			temp:=Calc(i);
			if ans>temp then
				begin
					ans:=temp;
					ansnum:=i;
				end;
		end;

	printf();

END.