代码开源在GitHub上,持续在此更新题解。
2007题注意输入的两组数据前后大小关系,一定要保持前一个数字小于第二个数字
2024/2/19
此次更新2010~2019题
着重注意2017,2018两题。
// Problem Description(2017)
// 对于给定的一个字符串,统计其中数字字符出现的次数。
// Input
// 输入数据有多行,第一行是一个整数n,表示测试实例的个数,后面跟着n行,每行包括一个由字母和数字组成的字符串。
// Output
// 对于每个测试实例,输出该串中数值的个数,每个输出占一行。
// Sample Input
// 2
// asdfasdf123123asdfasdf
// asdf111111111asdfasdfasdf
// Sample Output
// 6
// 9
#include <iostream>
#include <cstring>
using namespace std;
int main() {
int n;
cin>>n;
cin.ignore();
while (n--) {
char str[1000];
cin.getline(str, 1000);
int count = 0;
for (int i = 0; i < strlen(str); i++)
{
if (str[i]>='0'&&str[i]<='9')
{
count++;
}
}
cout << count << endl;
}
return 0;
}
这段代码若去掉cin.ignore()则会出现问题
问题出在使用 cin >> n 读取 n 的时候,输入缓冲区中的换行符没有被清除,导致第一个 getline() 读取了换行符而不是预期的字符串。
解决这个问题的方法之一是在读取 n 之后使用 cin.ignore() 来清除输入缓冲区中的换行符,或者直接使用 getline() 来读取 n。另外,也可以在读取字符串之前使用 cin.ignore() 来清除换行符。
// Problem Description
// 有一头母牛,它每年年初生一头小母牛。每头小母牛从第四个年头开始,每年年初也生一头小母牛。请编程实现在第n年的时候,共有多少头母牛?
// Input
// 输入数据由多个测试实例组成,每个测试实例占一行,包括一个整数n(0<n<55),n的含义如题目中描述。
// n=0表示输入数据的结束,不做处理。
// Output
// 对于每个测试实例,输出在第n年的时候母牛的数量。
// 每个输出占一行。
// Sample Input
// 2
// 4
// 5
// 0
// Sample Output
// 2
// 4
// 6
#include <iostream>
using namespace std;
int main()
{
int cows[55] = {0, 1, 2, 3, 4};
for (int i = 5; i < 55; i++)
{
cows[i] = cows[i - 1] + cows[i - 3];
}
int n;
while (cin >> n && n != 0)
{
cout << cows[n] << endl;
}
return 0;
}
这个问题为一个数学问题,在求解时可定义一个数组,采用递归的方式求出后续数组中数据的大小。类似斐波那契数列求解。
2024/2/23
Problem Description
输入一个字符串,判断其是否是C的合法标识符。
Input
输入数据包含多个测试实例,数据的第一行是一个整数n,表示测试实例的个数,然后是n行输入数据,每行是一个长度不超过50的字符串。
Output
对于每组输入数据,输出一行。如果输入数据是C的合法标识符,则输出"yes",否则,输出“no”。
Sample Input
3
12ajf
fi8x_a
ff ai_2
Sample Output
no
yes
no
这道题有一定坑。
以下为正确代码。
#include <iostream>
using namespace std;
bool isValidIdentifier(const char* str)
{
if (!((str[0] >= 'a' && str[0] <= 'z') || (str[0] >= 'A' && str[0] <= 'Z') || str[0] == '_'))
{
return false;
}
for (int i = 1; str[i] != '\0'; ++i)
{
if (!((str[i] >= 'a' && str[i] <= 'z') || (str[i] >= 'A' && str[i] <= 'Z') || (str[i] >= '0' && str[i] <= '9') || str[i] == '_'))
{
return false;
}
}
return true;
}
int main()
{
int n;
cin >> n;
cin.ignore();
for (int i = 0; i < n; ++i)
{
char str[51]; // 长度为50的字符串需要额外的一个字符存储'\0'
cin.getline(str, 51);
if (isValidIdentifier(str))
{
cout << "yes" << endl;
}
else
{
cout << "no" << endl;
}
}
return 0;
}
在isvalididentifier函数中,判断的条件改为if (!((str[0] >= 'a' && str[0] <= 'z') || (str[0] >= 'A' && str[0] <= 'Z') || str[0] == '_'))
后,调试无误
2024.2.28
2030题
Problem Description
统计给定文本文件中汉字的个数。
Input
输入文件首先包含一个整数n,表示测试实例的个数,然后是n段文本。
Output
对于每一段文本,输出其中的汉字的个数,每个测试实例的输出占一行.
[Hint:]从汉字机内码的特点考虑~
Sample Input
2
WaHaHa! WaHaHa! 今年过节不说话要说只说普通话WaHaHa! WaHaHa!
马上就要期末考试了Are you ready?
Sample Output
14
9
这道题在题目中建议从汉字机内码的特点考虑,但是机内码版代码运行不通过,所以改用ASCII码进行,汉字的ASCII码为负数,且一个汉字占用两个字节,所以在输出的时候注意除以2
#include <iostream>
#include <string>
using namespace std;
int main() {
int n;
cin >> n;
cin.ignore(); // 忽略换行符
while (n--) {
string a;
getline(cin, a);
int t = 0;
for (unsigned int i = 0; i < a.length(); ++i) {
if (a[i] < 0) {
t++;
}
}
cout << t/2 << endl;
}
return 0;
}
2031题
Problem Description
输入一个十进制数N,将它转换成R进制数输出。
Input
输入数据包含多个测试实例,每个测试实例包含两个整数N(32位整数)和R(2<=R<=16, R<>10)。
Output
为每个测试实例输出转换后的数,每个输出占一行。如果R大于10,则对应的数字规则参考16进制(比如,10用A表示,等等)。
Sample Input
7 2
23 12
-4 3
Sample Output
111
1B
-11
这道题难度也不小,以下为注释版代码。
#include <iostream>
#include <cmath>
#include <string>
using namespace std;
string turn(int n,int r)
{
string result="";
bool negative=false;
if(n<0)
{
negative=true;
n=-n;
}
while (n> 0) {
int remainder = n % r; // 求余数
char digit; // 用于存储当前位的数字字符
// 将余数转换为对应的字符表示
if (remainder < 10)
{
digit = remainder + '0'; // 如果余数小于10,直接使用对应的数字字符
}
else
{
digit = remainder - 10 + 'A'; // 如果余数大于等于10,使用对应的字母表示
}
result = digit + result; // 将当前位的字符插入到结果字符串的最前面
n /= r; // 更新N为除以R的结果
}
// 如果是负数,在结果字符串最前面添加负号
if (negative)
{
result = '-' + result;
}
return result;
}
int main()
{
int n;//10进制的数字
int r;//需要转换的进制
while(cin>>n>>r)
{
cout<<turn(n,r)<<endl;
}
return 0;
}
2035题
Problem Description
求A^B的最后三位数表示的整数。
说明:A^B的含义是“A的B次方”
Input
输入数据包含多个测试实例,每个实例占一行,由两个正整数A和B组成(1<=A,B<=10000),如果A=0, B=0,则表示输入数据的结束,不做处理。
Output
对于每个测试实例,请输出A^B的最后三位表示的整数,每个输出占一行。
Sample Input
2 3
12 6
6789 10000
0 0
Sample Output
8
984
1
这道题在数据较小时,使用cmath中的pow函数或者循环计算均可计算出正确结果,但是数据较大时,会造成溢出,所以需要构造一个函数,下面为示例代码。
#include <iostream>
using namespace std;
int fastPower(int a, int b)
{
int result = 1;
while (b)
{
if (b & 1)
{
result = (result * a) % 1000;
}
a = (a * a) % 1000;
b >>=1;//这里换成b=b-1不行
}
return result;
}
int main()
{
int a, b;
while (cin >> a >> b && (a != 0 || b != 0))
{
cout << fastPower(a, b) << endl;
}
return 0;
}
额外注意,在函数fastPower
中要使用b>>=1,b右移1,而不能单纯的b-1
在这段代码中,b >>= 1 和 b = b - 1 是两种不同的操作,分别对应着位运算的右移和数值的减法。
在 fastPower 函数中,b >>= 1 是为了将 b 向右移动一位,相当于将 b 除以2。这个操作是为了在快速幂算法中,利用指数 b 的二进制形式进行计算,以加快计算速度。
而将 b 替换为 b - 1 则是直接将 b 减去1。这样做会改变 b 的值,而不是对其进行右移操作。这对于快速幂算法是不正确的,因为快速幂算法的核心思想是利用指数 b 的二进制形式来减少乘法操作的数量,而不是简单地减小 b 的值。
因此,对于这段代码中的快速幂算法,应该保留 b >>= 1 而不是替换为 b = b - 1。
2039题
Problem Description
给定三条边,请你判断一下能不能组成一个三角形。
Input
输入数据第一行包含一个数M,接下有M行,每行一个实例,包含三个正数A,B,C。其中A,B,C <1000;
Output
对于每个测试实例,如果三条边长A,B,C能组成三角形的话,输出YES,否则NO。
Sample Input
2
1 2 3
2 2 2
Sample Output
NO
YES
这道题有坑,题目中给的是整型数据但是,一直报错,当改为double型运行通过
#include <iostream>
using namespace std;
int main()
{
int m;
cin>>m;
for(int i=0;i<m;i++)
{
double a,b,c;//这里不要用int虽然题目中样例给的是整数
cin>>a>>b>>c;
if(a+b>c&&a+c>b&&b+c>a&&a>0&&a<1000&&b>0&&b<1000&&c>0&&c<1000)
{
cout<<"YES"<<endl;
}
else
{
cout<<"NO"<<endl;
}
}
return 0;
}
2043
Problem Description
网上流传一句话:"常在网上飘啊,哪能不挨刀啊~"。其实要想能安安心心地上网其实也不难,学点安全知识就可以。
首先,我们就要设置一个安全的密码。那什么样的密码才叫安全的呢?一般来说一个比较安全的密码至少应该满足下面两个条件:
(1).密码长度大于等于8,且不要超过16。
(2).密码中的字符应该来自下面“字符类别”中四组中的至少三组。
这四个字符类别分别为:
1.大写字母:A,B,C...Z;
2.小写字母:a,b,c...z;
3.数字:0,1,2...9;
4.特殊符号:~,!,@,#,$,%,^;
给你一个密码,你的任务就是判断它是不是一个安全的密码。
Input
输入数据第一行包含一个数M,接下有M行,每行一个密码(长度最大可能为50),密码仅包括上面的四类字符。
Output
对于每个测试实例,判断这个密码是不是一个安全的密码,是的话输出YES,否则输出NO。
Sample Input
3
a1b2c3d4
Linle@ACM
^~^@^@!%
Sample Output
NO
YES
NO
这道题只需要遍历输入的字符串即可,然后看是否有三种类型的字符,或者使用cctype库中的函数也可以实现相同的效果
#include <iostream>
#include <string>
using namespace std;
bool isSafePassword(const string& password)
{
int length = password.length();
// 检查密码长度是否符合要求
if (length < 8 || length > 16)
{
return false;
}
// 初始化四组字符类别的标志
bool hasUpper = false, hasLower = false, hasDigit = false, hasSpecial = false;
// 遍历密码中的每个字符,检查其所属的字符类别
for (char ch : password)
{
if (ch >= 'A' && ch <= 'Z')
{
hasUpper = true;
}
else if (ch >= 'a' && ch <= 'z')
{
hasLower = true;
}
else if (ch >= '0' && ch <= '9')
{
hasDigit = true;
}
else if (ch == '~' || ch == '!' || ch == '@' || ch == '#' || ch == '$' || ch == '%' || ch == '^')
{
hasSpecial = true;
}
}
// 检查四组字符类别的标志是否至少有三个为true
int count = (hasUpper ? 1 : 0) + (hasLower ? 1 : 0) + (hasDigit ? 1 : 0) + (hasSpecial ? 1 : 0);
return count >= 3;
}
int main()
{
int M;
cin >> M;
cin.ignore(); // 忽略换行符
while (M--)
{
string password;
getline(cin, password);
if (isSafePassword(password))
{
cout << "YES" << endl;
}
else
{
cout << "NO" << endl;
}
}
return 0;
}
2044
Problem Description
有一只经过训练的蜜蜂只能爬向右侧相邻的蜂房,不能反向爬行。请编程计算蜜蜂从蜂房a爬到蜂房b的可能路线数。
其中,蜂房的结构如下所示。
Input
输入数据的第一行是一个整数N,表示测试实例的个数,然后是N 行数据,每行包含两个整数a和b(0<a<b<50)。
Output
对于每个测试实例,请输出蜜蜂从蜂房a爬到蜂房b的可能路线数,每个实例的输出占一行。
Sample Input
2
1 2
3 6
Sample Output
1
3
这道题需要注意,在存储路径时,要使用long long型,否则会造成溢出。
#include <iostream>
using namespace std;
int main()
{
int n;
cin>>n;
long long t[51];//由int改为long long结果变成了accepted,说明int型不能完全存储
t[0]=0,t[1]=1,t[2]=2;
for(int i=3;i<50;i++)
{
t[i]=t[i-1]+t[i-2];
}
while(n--)
{
int a,b;
cin>>a>>b;
cout<<t[b-a]<<endl;
}
return 0;
}
2045
有排成一行的n个方格,用红(Red)、粉(Pink)、绿(Green)三色涂每个格子,
每格涂一色,要求任何相邻的方格不能同色,且首尾两格也不同色.求全部的满足要求的涂法.
以上就是著名的RPG难题.
Input
输入数据包含多个测试实例,每个测试实例占一行,由一个整数N组成,(0<n<=50)。
Output
对于每个测试实例,请输出全部的满足要求的涂法,每个实例的输出占一行。
Sample Input
1
2
Sample Output
3
6
#include <iostream>
using namespace std;
int main() {
int n;
long long array[51];
array[1] = 3;
array[2] = 6;
array[3] = 6;
for (int i = 4; i < 51; i++)
array[i] = array[i - 2] * 2 + array[i - 1];
while (cin >> n)
cout << array[n] << endl;
return 0;
}
关于为什么array[i] = array[i - 2] * 2 + array[i - 1];
因为array[i-1]时,首尾不同色,且相邻不同色,所有添加一个颜色只能是一种。而当剩余两个格子未涂色时最后一个格子不能与第一个相同,那么颜色已经确定,那么倒数第二个格子有两种涂色情况。
2024.3.6
Problem Description
今年的ACM暑期集训队一共有18人,分为6支队伍。其中有一个叫做EOF的队伍,由04级的阿牛、
XC以及05级的COY组成。在共同的集训生活中,大家建立了深厚的友谊,阿牛准备做点什么来纪念这段激情燃烧的岁月,想了一想,
阿牛从家里拿来了一块上等的牛肉干,准备在上面刻下一个长度为n的只由"E" "O" "F"三种字符组成的字符串
(可以只有其中一种或两种字符,但绝对不能有其他字符),
阿牛同时禁止在串中出现O相邻的情况,他认为,"OO"看起来就像发怒的眼睛,效果不好。
你,NEW ACMer,EOF的崇拜者,能帮阿牛算一下一共有多少种满足要求的不同的字符串吗?
PS: 阿牛还有一个小秘密,就是准备把这个刻有 EOF的牛肉干,作为神秘礼物献给杭电五十周年校庆,
可以想象,当校长接过这块牛肉干的时候该有多高兴!这里,请允许我代表杭电的ACMer向阿牛表示感谢!
再次感谢!
Input
输入数据包含多个测试实例,每个测试实例占一行,由一个整数n组成,(0<n<40)。
Output
对于每个测试实例,请输出全部的满足要求的涂法,每个实例的输出占一行。
Sample Input
1
2
Sample Output
3
8
#include <iostream>
using namespace std;
int main()
{
int n;
long long p[40];
p[0]=0,p[1]=3,p[2]=8;
for(int i=3;i<40;i++)
{
p[i]=(p[i-1]+p[i-2])*2;//p[i-1]是以E,O,F任意一个字符结尾的可以直接添加E或F但是不能直接加O,//p[i-2]是以O结尾的只能添加E或F
}
while(cin>>n)
{
cout<<p[n]<<endl;
}
return 0;
}
先数出前2项所有可能组合的个数,再进行递推,发现p[i]=(p[i-1]+p[i-2])*2;
p[i-1]是以E,O,F任意一个字符结尾的可以直接添加E或F但是不能直接加O,p[i-2]是以O结尾的只能添加E或F,*2即可。
2048题之前多采用斐波那契数列的思想,即当前项为前两项的和或差再进行适当的变形。
而2048题之后,则主要考虑错排思想。
简要介绍错排:首先,对于D(n),有1~n这样n个元素错排,所以对于第一个元素①,它现在可能的位置有 (n-1)个。 这是递推公式需要乘(n-1)的原因
倘若它在第k个元素的位置上,对于第k个元素而言,它所在的位置就有两种可能—第一种,它处在非第一个元素①位置上,
所以对于接下来的排列就相当于是n-1个元素的错排,即D(n-1);第二种,它处在第一个元素①的位置上,
所以在排列D(n)中有两个元素找到了位置,那么接下来的队列就相当于是n-2个元素的错排。
因此,对于D(n)都有 D(n) = (n-1) [D(n-2) + D(n-1)]
特殊地,D(1) = 0, D(2) = 1
HDU 2006'10 ACM contest的颁奖晚会隆重开始了!
为了活跃气氛,组织者举行了一个别开生面、奖品丰厚的抽奖活动,这个活动的具体要求是这样的:
首先,所有参加晚会的人员都将一张写有自己名字的字条放入抽奖箱中;
然后,待所有字条加入完毕,每人从箱中取一个字条;
最后,如果取得的字条上写的就是自己的名字,那么“恭喜你,中奖了!”
大家可以想象一下当时的气氛之热烈,毕竟中奖者的奖品是大家梦寐以求的Twins签名照呀!
不过,正如所有试图设计的喜剧往往以悲剧结尾,这次抽奖活动最后竟然没有一个人中奖!
我的神、上帝以及老天爷呀,怎么会这样呢?
不过,先不要激动,现在问题来了,你能计算一下发生这种情况的概率吗?
不会算?难道你也想以悲剧结尾?!
Input
输入数据的第一行是一个整数C,表示测试实例的个数,然后是C 行数据,每行包含一个整数n(1<n<=20),表示参加抽奖的人数。
Output
对于每个测试实例,请输出发生这种情况的百分比,每个实例的输出占一行, 结果保留两位小 数(四舍五入),具体格式请参照sample output。
Sample Input
1
2
Sample Output
50.00%
#include <iostream>
#include <iomanip>
using namespace std;
long long a[20], b[20];
int main() {
a[0] = 0;
a[1] = 1;
b[0] = 1;
b[1] = 2;
for (int i = 2; i < 20; i++) {
b[i] = (i + 1) * b[i - 1];
a[i] = i * (a[i - 2] + a[i - 1]);
}
int n;
cin >> n;
while (n--) {
int x;
cin >> x;
cout << fixed << setprecision(2) << a[x - 1] * 100.0 / b[x - 1] << "%" << endl;
}
return 0;
}
在这段代码中,数组a用于存储错排的情况数,数组b用于存储所有可能的情况。最后计算概率直接相除即可。
国庆期间,省城HZ刚刚举行了一场盛大的集体婚礼,为了使婚礼进行的丰富一些,司仪临时想出了有一个有意思的节目,叫做"考新郎",具体的操作是这样的:
首先,给每位新娘打扮得几乎一模一样,并盖上大大的红盖头随机坐成一排;
然后,让各位新郎寻找自己的新娘.每人只准找一个,并且不允许多人找一个.
最后,揭开盖头,如果找错了对象就要当众跪搓衣板...
看来做新郎也不是容易的事情...
假设一共有N对新婚夫妇,其中有M个新郎找错了新娘,求发生这种情况一共有多少种可能.
Input
输入数据的第一行是一个整数C,表示测试实例的个数,然后是C行数据,每行包含两个整数N和M(1<M<=N<=20)。
Output
对于每个测试实例,请输出一共有多少种发生这种情况的可能,每个实例的输出占一行。
Sample Input
2
2 2
3 2
Sample Output
1
3
#include<iostream> //这道题的本质就是求解排列组合C(n,m)与错排m个元素D(m)的乘积
#include<cstdio>
using namespace std;
const int maxn = 25;
long long D[maxn], N[maxn]; /// D[i]表示错排数,N[i]表示全排列数(即n!)
int main()
{
D[0] = 0, N[0] = 1;
D[1] = 0, N[1] = 1;
D[2] = 1, N[2] = 2;//递推边界
for (int i = 3; i < maxn; i++)
{
D[i] = (i - 1)*(D[i - 1] + D[i - 2]);///错排递推关系式
N[i] = N[i - 1] * i;//求n,即所有可能的结果
}
int T, n, m;
cin >> T;
while (T--)
{
cin >> n >> m;
long long a = N[n] / (N[m] * N[n - m]); ///求C(n,m)组合数 n!/(m!*(n-m)!)
cout << D[m] * a << endl;
}
return 0;
}
2049题同理,不再进行赘述。