拍照
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;
}