POJ-3276:Face The Right Way

題目:

Farmer John has arranged his N (1 ≤ N ≤ 5,000) cows in a row and many of them are facing forward, like good cows. Some of them are facing backward, though, and he needs them all to face forward to make his life perfect.

Fortunately, FJ recently bought an automatic cow turning machine. Since he purchased the discount model, it must be irrevocably(不可改變地) preset to turn K (1 ≤ KN) cows at once, and it can only turn cows that are all standing next to each other in line. Each time the machine is used, it reverses the facing direction of a contiguous group of K cows in the line (one cannot use it on fewer than K cows, e.g., at the either end of the line of cows). Each cow remains in the same *location* as before, but ends up facing the *opposite direction*. A cow that starts out facing forward will be turned backward by the machine and vice-versa(反之亦然).

Because FJ must pick a single, never-changing value of K, please help him determine the minimum value of K that minimizes the number of operations required by the machine to make all the cows face forward. Also determine M, the minimum number of machine operations required to get all the cows facing forward using that value of K.

Input
Line 1: A single integer: N
Lines 2.. N+1: Line i+1 contains a single character, F or B, indicating whether cow i is facing forward or backward.
Output
Line 1: Two space-separated integers: K and M
Sample Input
7
B
B
F
B
F
B
B
Sample Output
3 3
Hint
For K = 3, the machine must be operated three times: turn cows (1,2,3), (3,4,5), and finally (5,6,7)

概譯:N頭牛有的朝前有的朝後,調轉牛方向的機器每次只能且必須改變相鄰的K頭牛,改變後牛的方向與之前相反,要求最後調轉得全部朝前。

輸入:N代表牛數量,F代表初始朝前,B代表初始朝後。

輸出:最小的調轉次數對應的K值,最小的調轉次數M。

分析:如果對K從1到N枚舉,對於每個K再遍歷牛羣,對於每隻牛需要調轉時再遍歷修改i到i+k-1,複雜度爲O(n³);如果我們對於是否反轉的過程進行優化,只用之前i被反轉次數的奇偶性來判斷它的朝向,則無需反覆修改,複雜度爲O(n²),詳細請見《挑戰程序設計競賽》151頁。更多解釋見代碼:

#include<cstdio>
#include<cstring>
#define inf 0x7fffffff

int n,K,M=inf,dir[5005],f[5005];
//K,M是最後要輸出的設定長度和翻轉次數,dir是初始方向,f是翻轉次數(只有0和1次) 
char ch;

int cal(int k)
{
	memset(f,0,sizeof(f));
	//最開始都是爲翻轉過 
	int sum=0,res=0;
	//sum是i-k+1到i範圍(長度爲k)內翻轉的次數和,res是總次數和即當前的m 
	for(int i=0;i+k-1<n;i++)//此過程用樣例在紙上寫寫有助理解 
	{
		if((sum+dir[i])&1)//sum是這一段翻過的次數,dir是本來朝向,如果加和是奇數代表朝後,要翻一次 
		{
			res++;
			f[i]=1;
		}
		//下面是對sum隨段移動的更新,加上當前的,減去馬上離開的 
		sum+=f[i];
		if(i-k+1>=0)
			sum-=f[i-k+1];
	}
	//對最後幾個進行檢查 
	for(int i=n-k+1;i<n;i++)
	{
		if((sum+dir[i])&1)//還有朝後的,由於要翻只能一段一起翻,這是最後一段已經翻過了,所以無解 
			return -1;
		if(i-k+1>=0)
			sum-=f[i-k+1];
	}
	return res;
}
int main()
{
	//輸入 
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
		getchar();
		ch=getchar();
		dir[i]=ch=='F'?0:1;
		//將朝前設置爲0,將朝後設置爲1,主要爲了核心部分取模而設置 
	}
	//枚舉k 
	for(int k=1;k<=n;k++)
	{
		int m=cal(k);//cal函數計算當前k所需m,若爲-1則意爲無解 
		if(m>=0&&m<M)//更新所需結果 
		{
			M=m;
			K=k;
		}
	}
	//輸出 
	printf("%d %d",K,M);
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章