POJ No.3276-FaceTheRightWay(反转、开关问题)

反转(开关问题)
Face The Right Way POJ No.3276

N头牛排成了一排,每头牛朝向前或者向后。为了让所有的牛都面向前方,农夫约翰买了一台自动转向的机器。这个机器在购买时就必须设定一个数值K,机器每操作一次,恰好使K头连续的牛转向。请求出为了让所有的牛都面向前方需要的最少的操作次数M和对应的最小的K。

限制条件:1<=N<=5000

输入

N = 7

BBFBFBB (F:面向前方,B:面向后方)

输出

K = 3

M = 3

1   B B FB F B B

2   F FB B F B B

3   F FF F B B B

4   F F F FF F F

分析:如果对每一头牛进行状态分析,则状态数有2^n个,无法在有限时间内找出答案。

1-区间反转的顺序对结果是没有影响的。

2-对同一区间进行两次反转是没有意义的。

-----> 问题变成了:求需要被反转的区间的集合

首先从最左端开始观察,包含这头牛的区间只有一个(连续区间长度是确定的

若最左端的牛面向前方,得:这个区间不需要反转

反之,若最左端的牛面向后方,则这个区间必须反转。 然后这个区间以后就不需要讨论了  (对同一区间进行两次反转是没有意义的

因为排除了对同一区间反复操作这种无意义的操作之后,只要存在让所有的牛都面向前方的方法,那么操作(区间反转的顺序对结果是没有影响的)可以唯一确定。

 

但如果我们要把所有的K都求一遍,对于每个K我们都要从最左端来考虑N头牛的情况。在最坏的情况下需要进行N-K+1次的反转操作,而每次操作又要反转K头牛。所以复杂度就是O(N^3)不能再有限时间内解决。

所以要对其进行优化。


优化:对区间反转的部分进行优化。

设F(i)代表区间[i,i+K-1]是否进行了反转,若反转则为1,否则为0

这样在考虑第i头牛的时候,若f[j]为奇数的话,那么这头牛的方向与起始方向是相反的,否则方向不变。

 

因为

 

所以每次都可以用常数时间计算出来,复杂度降低为O(N^2)

可以计算。

import java.util.Scanner;

public class FaceTheRightWay {

	public static void main(String[] args) {
		Scanner cin = new Scanner(System.in);
		System.out.print("N=");
		int N = cin.nextInt();
		cin.nextLine();
		String cow = new String();
		cow = cin.nextLine(); // 输入牛的方向
		int[] StateOfCow = new int[N];
		for (int i = 0; i < N; i++) {
			if (cow.charAt(i) == 'F')
				StateOfCow[i] = 0;
			else if (cow.charAt(i) == 'B')
				StateOfCow[i] = 1;
		}
		// solve
		int K = 1, M = N;
		for (int k = 1; k <= N; k++) {
			int m;
			m = cal(k, N, StateOfCow);
			if (m >= 0 && M > m) {
				M = m;
				K = k;
			}
		}
		System.out.println("K=" + K);
		System.out.println("M=" + M);
	}

	static int cal(int k, int N, int[] StateOfCow) {
		// 区间[i,i+K-1]是否反转
		int[] f = new int[N];
		for (int n = 0; n < f.length; n++)
			f[n] = 0;

		int res = 0;
		int sum = 0; // f的和
		for (int i = 0; i + k <= N; i++) { // 计算区间[i,i+k-1]
			if ((StateOfCow[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 ((StateOfCow[i] + sum) % 2 != 0)
				return -1; // 无解
			if (i - k + 1 >= 0)
				sum -= f[i - k + 1];
		}
		return res;
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值