题目链接:
http://poj.org/problem?id=3276
解题思路:
题目大意:
n头牛拍成了一列。每头牛或者向前或者向后。为了让所有的牛都面向前方,农夫约翰买了一台自动转向的机器。这个机器在购买时
就必须设定一个数值K,机器每操作一次恰好使K头连续的牛转向。请求出为了让所有的牛都能面向前方需要的最小的操作次数M和
对应最小的K。
算法思想:
首先我们来看看对于一个特定的K如何求出让所有的牛面朝前方的最小操作次数。如果把牛的方向作为状态进行搜索的话,由于状态
数有2的n次方个,是无法在时限内找出答案的。那么不搜索的话要怎么办呢?
首先,交换区间反转的顺序对结果时没有影响的。此外知道对同一区间进行两次以上的反转是多余的。由此,问题转化成了求需要
被反转的区间的集合。于是我们先考虑一下最左端的牛,包含这头牛的区间只有一个,因此如果这头牛面朝前方,我们就能知道这
个区间不需要反转,反之,如果这头牛面朝后方,对应的区间就必须进行反转了。而且再此之后这个最左的区间就再也不需要考虑
了。这样一来,通过首先考虑最左端的牛,问题的规模就缩小了1.不断地重复下去,就可以无需搜索求出最小所需的反转次数了。
此外,通过上面的分析可以知道,忽略掉对同一区间重复反转这类多余操作之后,只要存在让所有牛都朝前的方法,那么操作就和
顺序无关可以唯一确定了。
AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n;
int dir[5005];//牛的方向(0:F, 1:B)
int f[5005];//区间[i,i+k-1]是否进行反转
//固定k,求对应的最小操作回数
//无解的话则返回-1
int cal(int k){
memset(f,0,sizeof(f));
int res = 0;
int sum = 0;//f的和
for(int i = 0; i+k <= n; i++){
//计算区间[i,i+k-1]
if((dir[i]+sum)%2 != 0){
//前端的牛面朝后方
res++;
f[i] = 1;
}
sum += f[i];
if(i - k +1 >= 0){
sum -= f[i-k+1];
}
}
//检查剩下的牛是否有朝后方的情况
for(int i = n-k+1; i < n; i++){
if((dir[i]+sum)%2 != 0){
//无解
return -1;
}
if(i-k+1 >= 0){
sum -= f[i-k+1];
}
}
return res;
}
void solve(){
int K = 1,M = n;
for(int k = 1; k <= n; k++){
int m = cal(k);
if(m >= 0 && M > m){
M = m;
K = k;
}
}
printf("%d %d\n",K,M);
}
int main(){
while(~scanf("%d",&n)){
char ch;
for(int i = 0; i < n; i++){
getchar();
scanf("%c",&ch);
if(ch == 'F')
dir[i] = 0;
else
dir[i] = 1;
}
solve();
}
return 0;
}
本文介绍了解决POJ 3276问题的一种算法思路,该问题涉及通过最少次数的操作使一排面向不同方向的牛全部面向前方。文中详细解释了如何避免不必要的状态搜索,并通过分析牛的位置和方向来确定最小操作次数。
457

被折叠的 条评论
为什么被折叠?



