Enum:Face The Right Way(POJ 3276)

                

                面朝大海,春暖花开

  题目大意:农夫有一群牛,牛排成了一排,现在需要把这些牛都面向正确的方向,农夫买了一个机器,一次可以处理k只牛,现在问你怎么处理这些牛才可以使操作数最小?

  这道题很有意思,其实这道题是著名的开关问题的变种,我们可以用模拟去做,但是总不能一个一个地去翻转,不然就是2^n的复杂度了,我们要想另外一些方法。

  我们知道,如果连续翻转同一片区域其实等于没有反转,再者,在翻转位置一样的情况下,先翻转谁其实没有太打大的关系,所以我们可以规定一个顺序(比如从左到右翻转),如果规定了一个方向翻转以后,我们可以翻转过后的左区域以后都不会受到影响,所以这样我们就可以像矩阵乘法一样把k不断增长来把复杂度降下去O(N^3)(K个长度,每个长度最多操作N个数,每个数翻转K次)但是这样对于N=5000来说还是太大了,我们要继续把复杂度降下去。

  我们利用以前那种偏序集的方法,我们规定一个东西flip[i]:=i~i+k-1的是否需要翻转,如果需要翻转则是1,否则就是0,同时定义背面为1,正面是0

  可以看到

    ∑((i+1)-k+1,i)flip[j]=∑(i-1,i-k+1)flip[j]+flip[i]-flip[i-k+1]

  那么我们就可以不断减去两端的方法来求得flip的值了,这是个常数时间,所以复杂度降为O(N^2)

  

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <functional>
 4 
 5 using namespace std;
 6 
 7 static int dir[5001], if_flip[5001];
 8 
 9 int solve(const int, const int);
10 
11 int main(void)//开关问题
12 {
13     int cows_sum, ans_k, ans_step, step;
14     char tmp;
15     while (~scanf("%d", &cows_sum))
16     {
17         getchar();
18         for (int i = 0; i < cows_sum; i++)
19         {
20             scanf("%c", &tmp);
21             dir[i] = tmp == 'B' ? 1 : 0;//0表示前面,1表示后面
22             getchar();
23         }
24         ans_step = cows_sum; ans_k = 1;
25         for (int k = 1; k <= cows_sum; k++)
26         {
27             step = solve(cows_sum, k);
28             if (step >= 0 && step < ans_step)
29             {
30                 ans_step = step;
31                 ans_k = k;
32             }
33         }
34         printf("%d %d
", ans_k, ans_step);
35     }
36     return 0;
37 }
38 
39 int solve(const int cows_sum,const int k)
40 {
41     //flip[i]:=i~i+k-1是否进行了翻转,翻转了就是1,否则就是0
42     int sum = 0, i, ans_step = 0;
43 
44     memset(if_flip, 0, sizeof(if_flip));
45     for (i = 0; i <= cows_sum - k; i++)
46     {
47         if ((dir[i] + sum) % 2 == 1)//表明经过一系列翻转(如存在)还是背面朝上,则继续翻转
48         {
49             ans_step++;
50             if_flip[i] = 1;
51         }
52         sum += if_flip[i];
53         if (i - k + 1>= 0)//注意这里是i-k+1!
54             sum -= if_flip[i - k + 1];
55     }
56     for (; i < cows_sum; i++)//检查,剩下的段是不能翻转了(小于k了,所以只用检查一下符不符合规则就好)
57     {
58         if ((dir[i] + sum) % 2 == 1)//说明还是有朝上的,说明这样的k是不行的
59         {
60             ans_step = -1;
61             break;
62         }
63         else if (i - k + 1>= 0)//注意这里是i-k+1!
64             sum -= if_flip[i - k + 1];
65     }
66     return ans_step;
67 }

  

原文地址:https://www.cnblogs.com/Philip-Tell-Truth/p/5152600.html