【反转】POJ3276

Face The Right Way

题意:N个牛 每个都有一定的方向 B背对 F表示头对着你 给你一个装置 每次可以选择连续的K个牛反转方向 问你如何选择K 使得操作数最少 k也应尽量小.

例子:   N=7   BBFBFBB   (F:前面   B:后面)    (红色的为要反转的)   此出K=3  M=3

B B F B F B B

F F B B F B B

F F F F B B B

F F F F F F F   成功了

如果用暴力的方法的话:   考虑N头牛的话最坏情况下要进行N-k+1次反转操作。

for(k=1;k<=N;k++)

for(i=1;i<=N;i++)

for(j=i;j<=i+k;j++)

因此复杂度为n^3  。

反转算法:

定义 f[i]:区间[i,i+k-1]进行反转的话就为1,否则为0

区间反转部分很好优化: 
在考虑第i头牛时候,如果∑i−1j=(i−K+1)f[j]∑j=(i−K+1)i−1f[j]和为奇数,就说明此时这个牛方向与最初相反。 
由于 
∑ij=(i+1)−K+1f[j]∑j=(i+1)−K+1if[j]=∑i−1j=(i−K+1)f[j]∑j=(i−K+1)i−1f[j]+f[i]-f[i-K+1]

所以这个每一次都可以用常数算出来,时间复杂度O(n^2)

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int N=5000+10;
int f[N],dir[N],n;
int solve(int k){
   int cnt=0,sum=0;//sum为f的和
   memset(f,0,sizeof(f));
   for(int i=1;i<=n-k+1;i++){
    if((dir[i]+sum)%2){
         cnt++;
         f[i]=1;
    }
    sum+=f[i];
    if(i-k+1>=1) sum-=f[i-k+1];
   }
   for(int i=n-k+2;i<=n;i++){//检查剩下的牛有没有朝后面的情况
      if((dir[i]+sum)%2) return n+1;
      if(i-k+1>=1) sum-=f[i-k+1];
   }
   return cnt;
}

int main(){
   while(~scanf("%d",&n)){
       for(int i=1;i<=n;i++){
          char c;scanf(" %c",&c);
          if(c==‘B‘) dir[i]=1;
       }
       int ansk,ansm=n,t;
       for(int i=1;i<=n;i++){
          t=solve(i);
          if(t<ansm){
              ansm=t;ansk=i;
          }
       }
       printf("%d %d\n",ansk,ansm);
   }
}

参考:
常用技巧精选.反转(开关问题) poj3276 3279 3185 1244

开关问题--反转 poj 3276

原文地址:https://www.cnblogs.com/Kohinur/p/9038032.html

时间: 2024-10-06 00:53:53

【反转】POJ3276的相关文章

挑战程序竞赛 反转开关 poj3276

这个我其实也没有看太懂它的证明过程. 1.若某一个位置被翻转了n次,则其实际上被翻转了n%2次. 2.分析易知翻转的顺序并不影响最终结果. 3.现在我们着眼于第1个位置,可知若要将第1个位置进行翻转只有翻转它自己,因为没有其他位置的翻转会引起它的翻转. 由①可知若第1个位置为1则必须且进行翻转(并将其后2个进行连带翻转)且以后不再进行翻转,因为再进行翻转就一共翻转了2次相当于没翻转. 然后着眼于第2个位置,由于第1个位置不再进行翻转,所以要想翻转第2个位置只有翻转它自己,因为没有其他位置的翻转会

poj3276 Face The Right Way(反转问题,好题)

https://vjudge.net/problem/POJ-3276 首先意识到,对一个区间进行两次及以上的反转是没有意义的,而且反转次序不影响最终结果. 有点像二分搜索时用的逐个试的方法,每次翻的个数从1~n,然后进入函数判断. 由于正反性可以很巧妙地利用计数的奇偶来判断,所有这里优化复杂度,用f[i]记录i~i+k-1是否翻转了,不断向右判断,如果是反面就反转接下来的一组,直至最后,最后剩的几个如果全正就说明可以,如果有反面就说明不行. 1 #include<iostream> 2 #i

反转 开关问题

首先考虑最左端的牛.包含这头牛的区间只有一个,因此如果这头牛面朝前方,这个区间不反转,面朝后方则反转.以此类推,逐渐缩小问题规模. 用数组j[i]=1代表区间[i,i+K-1]进行了反转  j[i]=0代表不反转. 如果一头牛之前被反转的次数为奇数,则朝向和刚开始相反,为偶数则相同. #include<iostream> #include<memory.h> using namespace std; int N,dir[5005]; int j[5005]; //标记区间[i,i-

1.5编程基础之循环控制_29:数字反转

/* 1.5编程基础之循环控制 29:数字反转 总时间限制: 1000ms 内存限制: 65536kB 描述 给定一个整数,请将该数各个位上数字反转得到一个新数. 新数也应满足整数的常见形式,即除非给定的原数为零, 否则反转后得到的新数的最高位数字不应为零(参见样例2). 输入 输入共 1 行,一个整数N. -1,000,000,000 ≤ N≤ 1,000,000,000. 输出 输出共 1 行,一个整数,表示反转后的新数. 样例输入 样例 #1: 123 样例 #2: -380 样例输出 样

拼接字符串;字符反转;统计大串中小串出现的次数

package Homework; import java.util.ArrayList;import java.util.Iterator;import java.util.List;import java.util.Scanner;/** * 把数组中的数据按照指定个格式拼接成一个字符串举例:int[] arr = {1,2,3}; 输出结果:[1, 2, 3] 字符串反转举例:键盘录入"abc" 输出结果:"cba" 统计大串中小串出现的次数举例:在字符串&q

反转波兰计算器

#include <iostream>using namespace std;typedef double stackEntry; const int overflow = 1;const int underflow = 2;const int success = 0; const int maxstack = 100;//栈的最大尺寸class stack{public: stack(); int pop(); int push(const stackEntry &item); in

iOS控制反转(IoC)与依赖注入(DI)的实现

背景 最近接触了一段时间的SpringMVC,对其控制反转(IoC)和依赖注入(DI)印象深刻,此后便一直在思考如何使用OC语言较好的实现这两个功能.Java语言自带的注解特性为IoC和DI带来了极大的方便,要在OC上较好的实现这两个功能,需要一些小小的技巧. 控制反转和依赖注入 控制反转 简单来说,将一个类对象的创建由手动new方式改为从IOC容器内获取,就是一种控制反转,例如我们现在要创建一个ClassA类,则常规方法为 ClassA *a = [ClassA new]; 如果使用控制反转,

工厂模式、控制反转及依赖注入

在介绍工厂模式与控制反转(Inversion of Control)及依赖注入(Dependency Injection)之前,先介绍下类的调用方法.目前调用方法总共有3种:1.自己创建:2.工厂模式:3.外部注入,其中外部注入即为控制反转/依赖注入模式(IoC/DI).我们可以用3个形象的东西来分别表示它们,就是new.get.set.顾名思义,new表示自己创建,get表示主动去取(即工厂),set表示是被别人送进来的(即注入),其中get和set分别表示了主动去取和等待送来两种截然相反的特

Reverse Integer - 反转一个int,溢出时返回0

Reverse Integer Reverse digits of an integer. Example1: x = 123, return 321Example2: x = -123, return -321 若反转的数溢出,直接返回0 可以用计算结果来判断溢出,也可以用因数来判断 Java代码实现: 1 public class ReverseInteger { 2 public static int reverseInt(int x){ 3 if (x == 0) { 4 return