Acwing4440. 照相

文章讲述了在Acwing平台上的一个编程问题,涉及奶牛序列的翻转操作,目标是使G奶牛尽可能多地位于偶数位置,同时减少翻转次数。文章提到了暴力递推和递归法两种尝试解题的思路,以及最后的正确解决方案,该方案关注相邻数字的不同情况来计算最少翻转次数。
摘要由CSDN通过智能技术生成

拍照

Acwing4440. 照相原题链接
2023.7.12

1 暴力递推

1递推法

思路是每一头奶牛都翻转一次

统计翻转前和翻转后 两次位于偶位置的G奶牛的数量,若是翻转后数量多,那么就保持反转 要是翻转后数量好,就再翻转回去

!!!注意,这是错误的代码,甚至样例都没有过

本菜菜还没有找到更正的方法,欢迎并跪求路过的大佬指出错误,谢谢!!

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>

using namespace std;
const int N=1001010;
int n;//奶牛的个数
char breed[N];//输入奶牛的序列
int res;//统计反转的次数
int sum1; //统计反转前G奶牛位于偶位置的个数
int sum2; //统计反转后G奶牛位于偶位置的个数
int main(){
    cin>>n;
    for(int i=0;i<n;i++)cin>>breed[i];
  
    for(int i=0;i<n;i++){
        if(breed[i]=='G'&&(i+1)%2==0)sum1++;
        //如果G奶牛位于偶位置,sum1++,统计个数

                  reverse(breed,breed+i);
                  res++;
 
            //判断反转后跟翻转前位于偶位置的G奶牛的个数
            for(int c=0;c<=i;c++){
                if(breed[c]=='G'&&(c+1)%2==0)
                sum2++;
            }
   
            if(sum1>=sum2){
                reverse(breed,breed+i);
                res--;
                //如果翻转后比反转少,那再翻转回去
                //对应反转次数回到没反转的时候
            }
            sum2=0;
        
    }
      //GGGHGHHGHHHGHG (反转前)
//   -> HGHGGGHGHHHGHG (反转后)
    // for(int i=0;i<n;i++)cout<<breed[i];
    cout<<res;
 
    return 0;
}

2 递归法(会超时)

这个方法的思路主要是用Acwing92. 递归实现指数型枚举的思路实现的

(也算是一种活学活用吧嘿嘿)(菜菜的自我幻想)

利用递归的思想,将所有种可能的组合进行判断,判断哪一种组合的G奶牛在偶数位置数目最多而且翻转次数最小

因为本菜菜不懂如何记录并使用上一次递归过程中的sum

就没办法在得出sum最大的时候,比较res最小

只好分了两个递归,一个记录最大sum(偶数位置G奶牛的最大数目)

一个记录符合sum的最小res(翻转次数)

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<limits.h>  //最大最小数 INT_MIN  INT_MAX

using namespace std;
const int N=110010;
int n; //奶牛的总数目
char breed[N];//输入奶牛的序列
int sum;//统计符合条件的奶牛数目(位于偶位置的G奶牛)
int res; //统计反转次数

int sum1;//统计最大偶位置的奶牛的数目

int minres=INT_MAX;//记录符合sum最大条件下的最小res;
//minres初始值要很大,因为用的是min函数,初始值如果为0 那么全是0


void Maxsum(int u){
   if(u>n-1){  //终止条件
       
   for(int i=0;i<n;i++)
   if(breed[i]=='G'&&i%2==1)sum++;  //统计站在偶数位置的奶牛
       
       sum1=max(sum,sum1);//记录可以站在偶数位置的G奶牛最多有多少头
         
       sum=0; //初始化下次递归过程的sum
       
              return ;//一定要记得强制返回哦
  
   }
   
        //原理参考Acwing92.递归实现指数型枚举
         Maxsum(u+1);  //不反转
         reverse(breed,breed+u+1);   //翻转
         Maxsum(u+1);
}

//写两个递归 第一个用来求出奶牛站在偶数位置的可能做大值
//           第二个用来求符合最大sum的最小res
void Minres(int u){

   if(u>n-1){  //终止条件

   for(int i=0;i<n;i++)
   if(breed[i]=='G'&&i%2==1)sum++;  //i是从0开始的

   if(sum!=sum1) {sum=0; return ; } //如果sum不是最大值,直接返回 省时间
   
      minres=min(res,minres); 

      res=0;//重新初始化sum和res不要影响下一次的res
      sum=0;
        return ;
   }
   
          Minres(u+1);  //不反转
          reverse(breed,breed+u+1);   //翻转  //reverse是左闭右开
          res++;   //只有翻转res才会+1
          Minres(u+1);
}

int main(){
    cin>>n>>breed;
    
    Maxsum(0);//从第零层开始
    sum=0;
    res=0;  //防止影响新递归

    Minres(0);
    
     cout<<minres;
     return 0;   
}   

注意reverse是左闭右开!!!!!!!!

3 正确思路分析

先看图

请添加图片描述

思路的是

我们发现,因为数据本身一定是偶数,如果我们把每两个数据看做一组,

比如11 00 10 01

每次翻转,小组内的结果与其他小组是互相独立的,是不会彼此影响的

所以可以安心的只考虑这一组的情况

我们发现,处于第二个位置,一定是偶数位置,处于第一个位置,一定是奇数位置(这里仅考虑一个小组内的情况),所以我们考虑可以把所有的第二个位置的G翻转到第一个位置。

但是GH表示太过于复杂,我们考虑数学方法里的换元,用1表示GH,用0

表示HG,HH和GG这两种情况无论如何翻转是都不会变的,所以我们只需要考虑将所有的1变成0即可

假设数据是001,我们可以把1前面的0都转为1

即成为111,再把所有的111转换成0

即为000

这样就得到了1变为0的操作

以此类推,后续的所有的1都可以由这种操作转换

最后我们发现,实际统计最少的翻转次数,我们只需要考虑两个相邻的数字是否相同,相同则不用翻转。不同则需要反转一次,

由此,我们只需要统计相邻数字不相同的情况即可

举例说明

101

实际有三个不同的间隔(我们自动给最后一个一后面虚拟添加一个0也就是1010)

所以只需要反转三次即可

4 正确代码

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>

using namespace std;

const int N=200100;
int n;   //奶牛的数目
char breed[N];//记录奶牛的序列
int arr[N];   // 记录0 1数串
int j;         //arr数组的下标索引
int res;       //记录符合条件的数目
int main(){
    cin>>n>>breed;

    for(int i=0;i<n;i+=2){
        //只考虑HG和GH这两种情况
        if(breed[i]=='G'&&breed[i+1]=='H')arr[j++]=1;
        if(breed[i]=='H'&&breed[i+1]=='G')arr[j++]=0;
    
    }
    
    for(int i=0;i<j;i++){  //按理说需要单独考虑最后一种,但是因为数组后面是许多个0,所以不用单独考虑
        if(arr[i]!=arr[i+1])res++;
    }
    cout<<res;
    return 0;
}
  • 7
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海风许愿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值