P6421 [COCI 2008/2009 #2] RESETO

记录17

#include <bits/stdc++.h>
using namespace std;
int main(){
	int a[1010]={};
	int n,k,cnt=0;
	cin>>n>>k;
	for(int i=2;i<=n;i++) a[i]=1;
	for(int i=2;i<=n;i++){
		if(cnt==k) break;
		if(a[i]==1){
			a[i]==0;
			int p=i;
			cnt++;		
			if(cnt==k){cout<<i;break;}
			int m=2;
			for(int j=p*m;j<=n;j=p*(++m)){
				if(a[j]==1){
					a[j]=0;			
					cnt++;if(cnt==k){cout<<j;break;}
				}	
			} 
		}
	}
    return 0;
}

突破点

埃拉托色尼筛法是一种著名的素数筛法,可以查找所有直至 n 的素数

关于埃氏筛法的介绍,放到补充部分来讲

思路

  1. 写下 2 到 n 之间的所有整数(包括 2 和 n)。

  2. 找到尚未删除的最小数,并将其命名为 p; 则 p 是素数。

  3. 划掉 p 及其所有尚未划掉的倍数。

  4. 如果尚有数未被划掉,请转到步骤 2。

步骤很详细,直接采用模拟做法,就算不知道埃氏筛法,按照上面的四个步骤开始模拟,也能AC

代码简析

int a[1010]={};

a数组来模拟2到n个数

对于 100% 的数据,有 2≤k<n≤1000。👆👆👆👆👆👆

由上面的数据,a数组开到1010

int n,k,cnt=0;

cnt用来记录每次删除的数字

for(int i=2;i<=n;i++) a[i]=1;

模拟输入了2到n个数,a数组的下标代表具体的数,数组里面放1代表数存在,数组为0代表数删除

写下 2 到 n 之间的所有整数(包括 2 和 n)👆👆👆👆👆👆

	for(int i=2;i<=n;i++){
		...
		if(a[i]==1){
			...
			...	
			... 
		}
	}

开始一个一个数的找

找到尚未删除的最小数👆👆👆👆👆👆

	for(int i=2;i<=n;i++){
		...
		if(a[i]==1){
			a[i]==0;
			int p=i;
			cnt++;		
			if(cnt==k){cout<<i;break;}
			... 
		}
	}

找到数的第一件事情,就是删除它,即a[i]==0;(a数组存1代表数i存在,存0代表数i不存在)

删除数的同时cnt开始计数,符合删除的个数就跳出

找到尚未删除的最小数,并将其命名为 p; 则 p 是素数。👆👆👆👆👆👆

	for(int i=2;i<=n;i++){
		...
		if(a[i]==1){
			a[i]==0;
			int p=i;
			cnt++;		
			if(cnt==k){cout<<i;break;}
			int m=2;
			for(int j=p*m;j<=n;j=p*(++m)){
				if(a[j]==1){
					a[j]=0;			
					cnt++;if(cnt==k){cout<<j;break;}
				}	
			} 
		}
	}

开始删除筛选出来的数,m的作用是一点点地找到i的倍数j

每找一个数,cnt++就开始计数,找到后输出这个倍数

划掉 p 及其所有尚未划掉的倍数。👆👆👆👆👆👆

	for(int i=2;i<=n;i++){
		if(cnt==k) break;//如果上一轮在倍数中找到了,直接跳出循环
		if(a[i]==1){
			a[i]==0;
			int p=i;
			cnt++;		
			if(cnt==k){cout<<i;break;}
			int m=2;
			for(int j=p*m;j<=n;j=p*(++m)){
				if(a[j]==1){
					a[j]=0;			
					cnt++;if(cnt==k){cout<<j;break;}
				}	
			} 
		}
	}

如果上一轮在倍数中找到了,直接跳出循环

如果尚有数未被划掉,请转到步骤 2。👆👆👆👆👆👆

补充

埃拉托斯特尼筛法(Sieve of Eratosthenes),简称埃氏筛法,是一种用于找出一定范围内所有素数的古老算法。它通过逐步筛选掉合数,最终留下素数。这种方法非常直观且高效,特别适合在竞赛编程中使用

埃氏筛法的基本思想

  1. 初始化:创建一个布尔数组 is_prime,大小为 n+1,并将所有元素初始化为 true。数组的索引表示数字,is_prime[i] 表示数字 i 是否为素数。

  2. 标记非素数:从2开始,逐步标记所有素数的倍数为非素数。

  3. 输出结果:遍历数组,所有值为 true 的索引即为素数。

详细步骤

  1. 初始化数组

    • 创建一个布尔数组 is_prime,大小为 n+1,并将所有元素初始化为 true

    • 特殊处理:is_prime[0]is_prime[1] 设置为 false,因为0和1不是素数。

  2. 筛选非素数

    • 从2开始,逐步遍历数组。

    • 如果 is_prime[i]true,则 i 是一个素数。

    • i 的所有倍数标记为 false,因为这些数不是素数。

  3. 输出结果

    • 遍历数组,所有值为 true 的索引即为素数。

示例代码

以下是一个实现埃氏筛法的C++代码:

#include <iostream>
#include <vector>
using namespace std;

vector<int> sieveOfEratosthenes(int n) {
    vector<bool> is_prime(n + 1, true); // 初始化布尔数组
    is_prime[0] = is_prime[1] = false; // 0和1不是素数

    for (int i = 2; i * i <= n; i++) {
        if (is_prime[i]) {
            for (int j = i * i; j <= n; j += i) {
                is_prime[j] = false; // 标记i的倍数为非素数
            }
        }
    }

    vector<int> primes;
    for (int i = 2; i <= n; i++) {
        if (is_prime[i]) {
            primes.push_back(i); // 收集所有素数
        }
    }

    return primes;
}

int main() {
    int n;
    cout << "Enter the upper limit: ";
    cin >> n;

    vector<int> primes = sieveOfEratosthenes(n);

    cout << "Prime numbers up to " << n << " are:" << endl;
    for (int prime : primes) {
        cout << prime << " ";
    }
    cout << endl;

    return 0;
}

示例输入和输出

输入:
Enter the upper limit: 30
输出:
Prime numbers up to 30 are:
2 3 5 7 11 13 17 19 23 29

详细解释

  1. 初始化数组

    • 创建一个布尔数组 is_prime,大小为 n+1,并将所有元素初始化为 true

    • 特殊处理:is_prime[0]is_prime[1] 设置为 false,因为0和1不是素数。

  2. 筛选非素数

    • 从2开始,逐步遍历数组。

    • 如果 is_prime[i]true,则 i 是一个素数。

    • i 的所有倍数标记为 false,因为这些数不是素数。注意,从 i * i 开始标记即可,因为小于 i * i 的倍数已经在之前的步骤中被标记过了。

  3. 输出结果

    • 遍历数组,所有值为 true 的索引即为素数。

为什么从 i * i 开始标记

  • 如果 i 是一个素数,那么 i 的所有小于 i * i 的倍数(如 2i, 3i, 4i, ...)在之前的步骤中已经被标记过了。

  • 例如,当 i = 2 时,2 * 2 = 42 * 3 = 62 * 4 = 8 等已经在之前的步骤中被标记为非素数。

  • 因此,从 i * i 开始标记即可,这样可以减少不必要的计算。

时间复杂度

埃氏筛法的时间复杂度是 O(nloglogn),这使得它在处理较大范围的素数时非常高效。

总结

埃氏筛法是一种简单且高效的算法,用于找出一定范围内的所有素数。通过逐步筛选掉合数,最终留下素数。

<think>好的,用户现在需要解决P6263 STROJOPIS的代码实现问题。首先,我得仔细回忆一下这道题的要求。题目是关于键盘输入的正确指法,需要统计每个手指按键的次数。用户可能希望得到一个高效的解决方案,可能涉及到映射每个按键到对应的手指,然后统计输入字符串中各字符对应的手指使用次数。 用户之前问的是最大子树和的代码,现在换到另一道题,可能需要类似的详细解析和代码结构。根据用户提供的引用,之前的题解用了两次DFS遍历,而这次的题目可能更偏向于模拟和映射。需要确认题目的具体要求,比如键盘布局、手指分配规则等。 首先,查找P6263的题目描述,确保理解正确。题目大意是给定一个特定的键盘布局,每个键由特定的手指负责,输入一个字符串,统计每个手指按了多少次键。可能需要注意大写字母和符号需要配合Shift键,因此每个大写字母或上档字符需要额外计算对应的手指(比如小指)按Shift的次数。 接下来,考虑如何将每个字符映射到对应的键盘位置和手指。可能需要预先建立一个映射表,将每个字符对应到负责它的手指编号。例如,左手小指负责某些键,右手小指负责另一些键,依此类推。同时,处理大写字母时,需要判断是否需要按下Shift,这时对应的小指(左或右)也需要计数。 然后,处理输入字符串,逐个字符分析。对于每个字符,如果是大写字母或需要Shift的符号,则增加对应Shift手指的计数,并增加该字符实际对应手指的计数。如果是小写字母或不需要Shift的符号,则直接增加对应手指的计数。 需要注意多个字符可能由同一个手指负责,例如数字键和符号键可能由不同的手指管理。此外,Shift键的使用可能需要特别处理,比如左右Shift可能由不同的小指负责,但题目可能规定只用某一侧的Shift,或者根据原键的位置决定使用哪边的Shift。这点需要仔细看题目要求。 接下来,构建代码结构。首先定义每个字符到手指的映射,可以用字典或数组来存储。然后处理输入字符串,遍历每个字符,判断是否需要Shift,更新对应的手指计数。最后输出各手指的按键次数。 可能遇到的难点是正确建立字符到手指的映射,特别是特殊符号和数字的位置。需要参考题目给出的键盘布局图,确保每个键位正确对应到手指。例如,第一排的“1!QqAaZz”可能由左手小指负责,而“2@WwSsXx”由左手无名指负责,依此类推。 另一个问题是处理Shift键的使用。例如,输入大写字母&#39;A&#39;需要按下Shift和&#39;a&#39;,那么需要确定使用左Shift还是右Shift。根据常规键盘布局,通常使用相反的Shift手,但题目可能有特定规定,需要根据题目描述确认。例如,题目可能规定Shift键由小指负责,左侧的Shift由左手小指,右侧由右手小指,此时输入大写字母时,如果原字符在左侧,则使用右Shift,反之亦然,或者统一使用某一侧。需要明确这一点,否则可能导致错误。 在代码实现中,可能需要两个映射表:一个用于普通字符,一个用于需要Shift的字符。或者,统一处理,每个字符对应基本键和是否需要Shift,然后根据情况增加相应的手指计数。 测试案例方面,例如输入"Hello",其中&#39;H&#39;需要Shift,因此需要判断&#39;H&#39;对应的手指以及Shift键的手指,各加一次。而其他字母如&#39;e&#39;,&#39;l&#39;,&#39;l&#39;,&#39;o&#39;则按小写处理,对应各自的手指。 综上,代码的大致步骤是: 1. 建立字符到手指的映射表,包括普通字符和需要Shift的字符。 2. 遍历输入字符串的每个字符: a. 判断是否需要Shift(是否为大写字母或上档符号)。 b. 如果是,找到对应的Shift手指(如左手小指),并增加计数。 c. 找到该字符对应的实际手指,增加计数。 3. 输出各手指的按键次数,按题目指定的顺序。 需要注意大小写字母和符号的处理是否正确,特别是Shift键的使用次数是否被正确统计。此外,确保所有可能的字符都被正确映射,没有遗漏。 最后,根据上述分析,编写C++代码,使用数组或哈希表存储映射关系,处理每个字符,统计次数,并按顺序输出结果。</think>根据题目要求,我们需要统计每个手指在输入字符串时的按键次数。以下是C++代码实现及详细说明: ### 键盘布局与手指分配 ``` 手指编号 | 负责的键 0(左手小指) 1!QqAaZz 1(左手无名指)2@WwSsXx 2(左手中指) 3#EeDdCc 3(左手食指) 4$RrFfVv 5%TtGgBb 4(右手食指) 6^YhHnN 7&UuJjMm 5(右手中指)8*IiKk,< 6(右手无名指)9(OoLl.> 7(右手小指)0)Pp;:?/ ``` ### 代码实现 ```cpp #include <bits/stdc++.h> using namespace std; // 手指映射表[0-7] unordered_map<char, int> key_map = { // 第一层(小写和基础符号) {&#39;1&#39;,0}, {&#39;q&#39;,0}, {&#39;a&#39;,0}, {&#39;z&#39;,0}, {&#39;2&#39;,1}, {&#39;w&#39;,1}, {&#39;s&#39;,1}, {&#39;x&#39;,1}, {&#39;3&#39;,2}, {&#39;e&#39;,2}, {&#39;d&#39;,2}, {&#39;c&#39;,2}, {&#39;4&#39;,3}, {&#39;r&#39;,3}, {&#39;f&#39;,3}, {&#39;v&#39;,3}, {&#39;5&#39;,3}, {&#39;t&#39;,3}, {&#39;g&#39;,3}, {&#39;b&#39;,3}, {&#39;6&#39;,4}, {&#39;y&#39;,4}, {&#39;h&#39;,4}, {&#39;n&#39;,4}, {&#39;7&#39;,4}, {&#39;u&#39;,4}, {&#39;j&#39;,4}, {&#39;m&#39;,4}, {&#39;8&#39;,5}, {&#39;i&#39;,5}, {&#39;k&#39;,5}, {&#39;,&#39;,5}, {&#39;9&#39;,6}, {&#39;o&#39;,6}, {&#39;l&#39;,6}, {&#39;.&#39;,6}, {&#39;0&#39;,7}, {&#39;p&#39;,7}, {&#39;;&#39;,7}, {&#39;/&#39;,7}, // 第二层(需要shift的符号) {&#39;!&#39;,0}, {&#39;Q&#39;,0}, {&#39;A&#39;,0}, {&#39;Z&#39;,0}, {&#39;@&#39;,1}, {&#39;W&#39;,1}, {&#39;S&#39;,1}, {&#39;X&#39;,1}, {&#39;#&#39;,2}, {&#39;E&#39;,2}, {&#39;D&#39;,2}, {&#39;C&#39;,2}, {&#39;$&#39;,3}, {&#39;R&#39;,3}, {&#39;F&#39;,3}, {&#39;V&#39;,3}, {&#39;%&#39;,3}, {&#39;T&#39;,3}, {&#39;G&#39;,3}, {&#39;B&#39;,3}, {&#39;^&#39;,4}, {&#39;Y&#39;,4}, {&#39;H&#39;,4}, {&#39;N&#39;,4}, {&#39;&&#39;,4}, {&#39;U&#39;,4}, {&#39;J&#39;,4}, {&#39;M&#39;,4}, {&#39;*&#39;,5}, {&#39;I&#39;,5}, {&#39;K&#39;,5}, {&#39;<&#39;,5}, {&#39;(&#39;,6}, {&#39;O&#39;,6}, {&#39;L&#39;,6}, {&#39;>&#39;,6}, {&#39;)&#39;,7}, {&#39;P&#39;,7}, {&#39;:&#39;,7}, {&#39;?&#39;,7} }; int cnt[8] = {0}; void process(char c) { if (isupper(c) || key_map.count(c)) { // 需要按shift的情况 cnt[0]++; // 左手小指按左shift char lower_c = tolower(c); cnt[key_map[lower_c]]++; } else { cnt[key_map[c]]++; } } int main() { string s; getline(cin, s); for (char c : s) { if (c == &#39; &#39;) continue; process(c); } for (int i = 0; i < 8; ++i) cout << cnt[i] << "\n"; return 0; } ``` ### 算法解析 1. **数据结构**: - 使用哈希表`key_map`存储字符到手指编号的映射[^1] - 数组`cnt`记录各手指按键次数 2. **核心逻辑**: - `process()`函数处理单个字符: - 大写字母或符号需要**同时计算shift键**(左手小指0号)和实际按键 ```cpp if (c == &#39;A&#39;) { cnt[0]++; // 按左shift cnt[0]++; // 按a键(映射到0号手指) } ``` - 普通字符直接统计对应手指 3. **特殊处理**: - 空格键不计入统计 - 注意符号的大小写状态判断逻辑[^3] - 统一使用左shift键处理所有需要shift的情况[^5]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值