技巧问题

本文详细解析了两种经典算法问题:尺取法求连续子序列的最小长度,确保其总和不小于给定值;以及解决所有牛面朝前的最小操作数问题,通过连续翻转牛的方向。提供了完整的C++代码实现。

一:尺取法

/**
尺取法,给定长度为n的数列a(0~n-1),以及整数S,求出总和不小于S的连续子序列
的长度的最小值 
输入:
10 15
5 1 3 5 10 7 4 9 2 8 
输出:
2 
**/
#include<stdio.h>
#include<algorithm>
using namespace std;
int n,S;
int a[100];
int sum[100];
void solve(){
	//计算sum
	for(int i=0;i<n;i++){
		sum[i+1]=sum[i]+a[i];
	} 
	if(sum[n]<S){
		//解不存在 
		printf("0\n");
		return ;
	}
	int res=n;
	for(int s=0;sum[s]+S<=sum[n];s++){
		int t=lower_bound(sum+s,sum+n,sum[s]+S)-sum;
		res=min(res,t-s);
	} 
	printf("%d\n",res);
} 
int main(){
	scanf("%d%d",&n,&S);
	for(int i=0;i<n;i++){
		scanf("%d",&a[i]);
	}
	solve();
	return 0;
}

二:翻转(开关问题)

/**
有N头牛,每头牛要么面朝前方(F)或者面朝后方(B)。现在可以连续驱使连续的K头牛反转。求使得所有的牛面朝前方的最小操作数
和对应的K是多少?
输入:
7
BBFBFBB
输出:
3 3 
**/ 
#include<stdio.h>
#include<string.h> 
int N;
int dir[100];  //牛的方向(0:F,1:B) 
int f[100]; //区间[i,i+k-1]  //是否进行反转
//固定K,求对应的最小操作回数
//无解的话则返回-1
int calc(int K){
	
	memset(f,0,sizeof(f));
	int res=0;
	int sum=0;  //能影响到当前点的f和 
	for(int i=0;i+K<=N;i++){
		//计算区间[i,i+K-1]
		if((dir[i]+sum)%2!=0){
			//当sum为偶数时以前的反转对当前无影响,dir为1时,然后结合dir[i]最初状态 
			//当sum为奇数时以前的反转使状态与原始相反,然后结合dir[i]最初状态 
			res++; //翻转次数加1 
			f[i]=1; //记录区间[i,i-K+1]翻转 
		} 
		sum+=f[i]; //加上f[i],以便判断当前翻转是否对下一个翻转造成影响 
		if(i-K+1>=0){
			sum-=f[i-K+1];//减去将对下一个区间没有影响的f[i] 
		} 
	} 
	//检查剩下的牛是否有面朝后面的情况
	for(int i=N-K+1;i<N;i++){
		if((dir[i]+sum)%2!=0){
			//无法翻转但是需要翻转,无解
			return -1; 
		} 
		if(i-K+1>=0){
			sum-=f[i-K+1];
		} 
	} 
	return res;
}
void solve(){
	int K=1,M=N;
	for(int k=1;k<=N;k++){
		int m=calc(k);
		if(m>=0&&M>m){
			M=m;
			K=k;
		}
	}
	printf("%d %d\n",K,M);
} 
int main(){
	char str[100];
	scanf("%d",&N);
	scanf("%s",str);
	for(int i=0;i<N;i++){
		if(str[i]=='F'){
			dir[i]=0;
		}else{
			dir[i]=1;
		}
	}
	solve();
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值