熄灭灯泡问题(C语言蓝桥杯模拟题,时间复杂度仅O(n))

本文探讨了如何通过最少的操作次数将一组灯泡从初始状态转变为全关闭状态,利用化简思想,揭示了选择次数与灯泡状态中‘1’的数量之间的规律。关键在于理解如何通过化简将复杂状态简化为‘010101’模式,从而计算出实际操作次数。

问题描述
小华面前有一排控制灯泡的开关,0表示关,1表示开,他每次会选择一个位置n,然后把 n位置的开关全部都按一次。给定开关的初始状态,用 01字符串表示,长度不超过100000,小华至少选几次,把所有的灯泡都熄灭(即所有开关都为0)?
样例输入

100
1010011
样例输出

1
5


举例子解释一下题目:

假设有5个灯泡。状态是01010,如果把灯泡全部熄灭,那就是全部变成00000。那么,如何选择位置呢?
因为每选择一次位置n,都会让第1个到第n个的灯泡的状态发生改变,所以,每次都从最前面开始选择,一步一步地让灯泡逐步变成相同状态,以此类推最后一次全部灯泡都从1状态变成0状态。

example 1:
初始:01010
第一次:11010(选择第一个位置)
第二次:00010(选择第二个位置)
第三次:11110(选择第三个位置)
第四次:00000(选择第四个位置)
选择次数为4。

example 2:
初始:1011101
第一次:0011101(选择第一个位置)
第二次:1111101(选择第二个位置)
第三次:0000001(选择第五个位置)
第四次:1111111(选择第六个位置)
第五次:0000000(选择第七个位置)
选择次数为5。

用程序验证答案
如果不把前面的灯泡先变成相同的状态而直接跳过不同状态的灯泡选择后面的,必然会导致有的灯泡要重复做着无用的状态改变。例如情况“000100110”时,不选择第四个位置的“1”而直接选到第五个位置的“0”,就会变成这样:“111010110”。不难发现前五个位置的灯泡状态由“00010”变成“11101”,相对状态并没有改变,所以此次选择是无意义的。


既然这道题是要把1变成0,而且一次是改变一串。以example 2为例,第三第四第五个的“1”可以在一次选择中全部改变成“0”。这就意味着,任何情况都可以化简为“010101”或者“101010”的形式,也就是说,两个“0”之间无论有多少个“1”都可以化简为一个“1”,两个“1”之间无论有多少个“0”都可以化简为一个“0”。例如:“11000111111011001”就可以化简为“1010101”。

example 3:
初始:0011110110100111110011
化简后:0101010101
第一次:1101010101
第二次:0001010101
……
第九次:1111111111
第十次:0000000000
用程序验证答案
例子初始值经过化简后,变成5个“0”和5个“1”相间的字符串。

回顾一下example 1,整个字符串的选择次数和化简后的字符串的“1”或者“0”的数量关系或许可以用数学公式来表达(下面以“1”为例)。

//化简思想代码:
    char s[100000];
    int num;//记录字符串长度
    int flag;//记录化简后'1'的数量
    scanf("%s",s);
	num=strlen(s);//读取字符串长度
	for(i=0;i<num; ){
		if(s[i]=='1'){
		i++;//往下一个位置读取
		while(s[i]=='1')
		i++;//while//往下一个位置读取
		flag++;//记录下化简后有多少个“1”;
		}//if1
		else
		i++;//往下一个位置读取
	}//for

example 4:
初始:01010
第一次:11010
第二次:00010
第三次:11110
第四次:00000
“1”的数量:2
选择次数:4

example 5:
初始:010100110
化简:0101010
第一次:1101010
……
第五次:1111110
第六次:0000000
化简后“1”的数量:3
选择次数:6


嗯哼?莫非<选择次数=“1”的数量×2>?这难道只是个巧合吗?
那我们再来试几个例子:

example 6:
初始:0101
第一次:1101
第二次:0001
第三次:1111
第四次:0000
"1"的数量:2
选择次数:4

初始状态:1010
第一次:0010
第二次:1110
第三次:0000
“1”的数量:2
选择次数:3

初始:01010101
“1”的数量:4
选择次数:8

初始:10101010
“1”的数量:4
选择次数:7

初始:01
“1”的数量:1
选择次数:2

初始:10
“1”的数量:1
选择次数:1


其实啊,这个算法的整体的关键就在于,当化简后,最后有n个亮着的灯泡,要最后一个灯泡熄灭,那么最前方第一个灯泡就得变化2n次。

计算公式

当然,如果说次数是“1”化简后的次数的两倍明显是有失偏颇的。但是同样是m个“1”,有的情况是2m次,有的情况是2m-1次。那么为什么有的情况是奇数次,有的情况是偶数次呢?
至于相差1次,那就只需要看最前方第一个两者的灯泡的位置是在第一位还是第二位判别着看就行。
如果是“0”开头,若想要后边的“1”变成“0”,则必须先把开头的“0”先变成“1”,才能把开头的“0”后面的“1”变成“0”。而如果第一个灯泡是“1”,则直接就可以让它变成“0”,就可以直接少选择一次。而这个情况,无论字符串多长,都适用。

//附上判断计算代码:
	if(s[0]=='1')
	sum=flag*2-1;
	if(s[0]=='0')
	sum=flag*2;

附上完整代码:

最后,附上完整代码。敬请批评指正!

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
int main()
{
	char s[1000000];
	while(~scanf("%s",s))
	{	
	int num,sum,i=0,flag=0;
	num=strlen(s);
	for(i=0;i<num; ){
		if(s[i]=='1'){
		i++;
		while(s[i]=='1')
		i++;//while
		flag++;
		}//if1
		else
		i++;
	}
	if(s[0]=='1')
	sum=flag*2-1;
	if(s[0]=='0')
	sum=flag*2;
	cout<<"选择次数为:";
	cout<<sum<<endl;
	}
	return 0;
 } 
### FPGA蓝桥杯模拟试题及相关解析 #### 关于FPGA蓝桥杯竞赛 蓝桥杯大赛是一项面向全国高校学生的科技赛事,其中涉及嵌入式开发的部分常会考察参赛者对FPGA技术的理解与应用能力。FPGA(Field Programmable Gate Array)是一种可编程逻辑器件,在电子设计自动化领域具有重要地位。以下是几道典型的FPGA蓝桥杯相关题目及其解析。 --- #### 题目一:LED灯闪烁控制 **描述**: 使用Verilog HDL编写一段程序实现如下功能——当按键按下时,8个LED灯依次点亮并熄灭;松开按键后停止操作。 **解答**: 此题主要考查学生对于状态机的设计以及同步电路的应用。 ```verilog module led_control( input clk, input btn, // 按键信号 output reg [7:0] leds ); reg [3:0] counter; always @(posedge clk or posedge btn) begin if (!btn) begin leds <= 8'b0; counter <= 4'd0; end else begin case(counter) 4'd0 : leds <= 8'b0000_0001; 4'd1 : leds <= 8'b0000_0010; 4'd2 : leds <= 8'b0000_0100; 4'd3 : leds <= 8'b0000_1000; 4'd4 : leds <= 8'b0001_0000; 4'd5 : leds <= 8'b0010_0000; 4'd6 : leds <= 8'b0100_0000; 4'd7 : leds <= 8'b1000_0000; default: leds <= 8'b0000_0000; endcase if (counter == 4'd7) begin counter <= 4'd0; end else begin counter <= counter + 1; end end end endmodule ``` 上述代码通过计数器`counter`来逐位改变LED的状态,并利用按键输入作为触发条件[^1]。 --- #### 题目二:七段数码管显示数字 **描述**: 编写一个模块用于驱动共阳极七段数码管,使其能够循环显示数字0到9。 **解答**: 该问题的核心在于如何将十进制数值转换成对应的七段码形式。 ```verilog module seven_segment_display ( input [3:0] digit_in, // 输入的BCD编码数据 output reg [6:0] segments // 输出给七段显示器的数据 ); always @(*) begin case(digit_in) 4'h0: segments = 7'b1000000; // 显示 '0' 4'h1: segments = 7'b1111001; // 显示 '1' 4'h2: segments = 7'b0100100; // 显示 '2' 4'h3: segments = 7'b0110000; // 显示 '3' 4'h4: segments = 7'b0011001; // 显示 '4' 4'h5: segments = 7'b0010010; // 显示 '5' 4'h6: segments = 7'b0000010; // 显示 '6' 4'h7: segments = 7'b1111000; // 显示 '7' 4'h8: segments = 7'b0000000; // 显示 '8' 4'h9: segments = 7'b0010000; // 显示 '9' default: segments = 7'b1111111; // 默认关闭所有段 endcase end endmodule ``` 这里采用了一个简单的组合逻辑映射表结构完成从BCD码至实际硬件接口电平之间的变换过程[^2]。 --- #### 题目三:基于FPGA的时间延迟生成器 **描述**: 设计一种方法可以在指定范围内随机产生一定长度的时间间隔,并将其应用于脉冲宽度调制(PWM)输出上。 **解答**: 时间延迟能够借助内部振荡源配合分频机制达成目标效果。 ```verilog module pwm_generator #(parameter WIDTH=8)( input wire clk_i, input wire rst_ni, input wire enable_i, input wire duty_cycle_i[WIDTH-1:0], output logic pwm_o ); logic [$clog2(WIDTH)-1:0] cnt_q[$]; assign pwm_o = &cnt_q ? ~enable_i | (&duty_cycle_i >> cnt_q) : !rst_ni ; initial $display("PWM Generator Initialized"); always_ff@(negedge clk_i,negedge rst_ni)begin:GEN_CNT_PROC if(!rst_ni || !enable_i)begin foreach(cnt_q[i])cnt_q[i]<=0; end else foreach(cnt_q[i])begin if(&cnt_q[i]==1'b1&&&duty_cycle_i>>i==1'b1)pwm_o<=~pwm_o; else if((&cnt_q[i])==1'b1&&!((duty_cycle_i>>i)==1'b1))null; else cnt_q[i]<=(cnt_q[i]+1)%($pow(2,i)); end end endmodule ``` 以上片段展示了如何构建动态调整占空比的功能单元[^3]。 --- #### 总结说明 针对不同类型的FPGA应用场景,考生需灵活运用基础理论知识解决具体工程实践中的各类挑战。无论是简单外设交互还是复杂算法实现都需要扎实掌握相应工具链及技巧才能取得理想成绩。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

芣苢的成长之路

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

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

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

打赏作者

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

抵扣说明:

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

余额充值