梳理hd百题知识点
(还有涉及到还涉及到二分查找、定义结构体类型变量的方法、 进制转换、错排、数组模拟等,稍后我会专门为这些整理)
一、以字符串的例题强调知识点
1.字符串每个的首字母变成大写
感谢 yzx 大佬
分析
- 如何输入多组含空格的字符串
- 如何确定每个单词的首字母
代码中涉及到的点
while(gets(str)!=EOF)
如果这样写, 会输出超限,gets读取到EOF会返回nall, 和scanf不同while(scanf("%s", str) != EOF)
读到空格就会结束,且这种字符串的输入不需要&
- 除第一个单词外每个单词的第一个字母前都有一个空格
- 在ASCII码中,小写字母减去相应大写字母 = 32,
例如:a - A = 32
自己的小破代码
#include <stdio.h>
int i, len;
char str[22000];
int main(void){
while(gets(str)){
//输入多组字符串样例这样写即可
len = strlen(str);
str[0] = str[0] - 32;
for(i = 1; i < len; i++){
if(i < len -1 && str[i] == ' ' && str[i+1] != ' '){
str[i+1] = str[i+1] - 32;
//从前往后,莫得特殊情况
//注意这里是 i+1, 下面不用写 else if(i== len-1)重复了
}
}
printf("%s\n",str);
}
return 0;
}
2.字符串的部分改变
- 题目简单描述: 当出现超过3个连续的 6 时, 需替换成 “9”, 如果
有超过 9 个连续的 6 时, 则将这串连续的 6 替换成 “hun yuan zhb”.
分析
- 开始不成熟想法: 尝试着填入一个新数组,但该新数组的大小和原按字符串大小可能不一样了, 没有想到如何直接操作,难免涉及到 下列思路。
- 我们可以直接输出, 涉及到
计录连续“6”的个数,
if分支等基础知识的应用
#include <stdio.h>
int n, i, j, sum, len;
char str[2000];
int main(void){
gets(str);
len = strlen(str);
for(i=0; i<len; i++){
if(str[i] == '6'){
sum++;
}
//记录连续“6”的个数
/*接下来讨论连续6的个数的不同情况*/
else if(sum <= 3 && str[i+1] != '6'){
for(j=0; j< sum; j++){
printf("6");
sum = 0; //记得 sum = 0
}
}
else if(sum > 3 && sum <= 9 && str[i+1] != '6'){
printf("9");
sum = 0;
}
else if(sum > 9 && str[i+1] != '6'){
printf("hun yuan zhb");
sum = 0;
}
else if(str[i] != '6'){
printf("%c",str[i]);
}
}
return 0;
}
3.统计汉字个数
分析
汉字是由两个小于0的字符组成,可以直接 用strlen
函数即可,在统计结果的时候就要将count除以2 即可
#include<stdio.h>
#include <string.h>
char str[900];
int count;
int main(void)
{
gets(str);
count = strlen(str);
printf("%d", count / 2);
return 0;
}
二、峰值原理
感谢 ysq 大佬
简单描述
在一天内可以进行多次交易(多次买卖一支股票),xxx必须在再次购买前出售掉之前的股票,求能获得的最大利润。主要就是考的峰值原理,这里也只是感觉峰值原理的应用,敲一下核心代码
峰值图
核心代码
//直接遍历
if(arr[i] < arr[i+1]){
sum += arr[i+1] - arr[i]
}
三、求n个数的最小公倍数
分析
先求两个数的最大公约数------辗转相除法
再求这两个数的最小公倍数,拿该数和其他数再求
#include <stdio.h>
int i, x, a, n, j;
int arr[100010];
int sy(int a, int b){
//辗转相除法
int r;
while(b != 0){
r = a % b;
a = b;
b = r;
}
return a;
}
int main( void ){
while(scanf("%d",&n)!=EOF){
i = 0;
while(n--){
scanf("%d",&arr[i]);
i++;
}
a = arr[0];//a 记录前几个的最小公倍数
for(j = 1; j < i; j++){
a = a / sy(a, arr[j]) * arr[j];
}
//先除以最小公约数,再乘另一个数,减少溢出的可能
printf("%d\n", a);
}
return 0;
}
另赠一个细节问题: 求 a 的 b 次方的后三位组成的数字
while(b--)
{
s=s*n;//如果先求出n的m次方,这个数可能会很大,导致溢出,所以每次两个数相乘后就对1000取余
s=s%1000;//因为是求后三位所以取余1000,其他以此类推
}
printf("%d\n", sum);
注意题目的说法: 该数是正数, 要考虑浮点数
四、涉及到数学几何问题
1.给出多边形的坐标(整数),求面积
分析
该处只想记录一个公式(具体怎么推导,不晓得啊)
for(i=0; i < j-1; i++){
sum += abs(arr[i].x * arr[i+1].y - arr[i+1].x * arr[i].y);
//可以简单这样记 x1 * y2 - x2 * y1
}
sum += abs(arr[j-1].x * arr[0].y - arr[0].x * arr[j-1].y);
sum = 1.0 * sum / 2; //sum 定义成double类型的
2.输入两矩形对角线坐标,输出交叉部分面积
如没有相交部分,输出0.00即可
分析
1.没有告诉我们输入的是矩形的哪条对角线,需要转化统一。
2.判断相交时坐标特点
#include <stdio.h>
double a1, b1, a2, b2, a3, b3, a4, b4, x1, y1, x2, y2;
int main(void){
while(scanf("%lf %lf %lf %lf %lf %lf %lf %lf", &a1, &b1, &a2, &b2, &a3, &b3, &a4, &b4) != EOF){
//统一:x1,y1为左下角, x2, y2右上角,,
//需要判断大小关系,转化一下即可。
if(a1 > a2){
x1 = a1;
a1 = a2;
a2 = x1;
}
if(b1 > b2){
y1 = b1;
b1 = b2;
b2 = y1;
}
if(a3 > a4){
x1 = a3;
a3 = a4;
a4 = x1;
}
if(b3 > b4){
y1 = b3;
b3 = b4;
b4 = y1;
}
//求相交图形的坐标
x1 = a1 > a3 ? a1 : a3;
y1 = b1 > b3 ? b1 : b3;
x2 = a2 > a4 ? a4 : a2;
y2 = b2 > b4 ? b4 : b2;
if(x1 < x2 && y1 < y2){
printf("%.2f\n", (x2 - x1) * (y2 - y1));
}
else{
printf("0.00\n");
}
}
return 0;
}
3.无限的路
(看图形,观察规律)
样例
2
0 0 0 1
0 0 1 0
/*输出
1.000
2.414*/
规律
横纵坐标相加,得出的就是最高点的坐标,而且代表着是第几条倾角为145度的斜线,相应代表这里有几个根号二长度。
#include <stdio.h>
#include <math.h>
int x, y, k, l, n, p;
double sum1, sum2;
int main(void){
scanf("%d", &n);
while(n--){
sum1 = 0.0;
sum2 = 0.0;
scanf("%d %d %d %d", &x, &y, &k, &l);
p = x + y;
while(p--){
sum1 += (sqrt(2) * p);
sum1 += sqrt(p * p + (p+1) *(p+1)); //该处加的是两点间的直线段
}
sum1 += (sqrt(2) * x); //算出所在边的折线的长度
p = k + l;
while(p--){
sum2 += (sqrt(2) * p);
sum2 += (sqrt((p+1)*(p+1) + p * p));
}
sum2 += (sqrt(2) * k);
printf("%.3f\n", fabs(sum1 - sum2)); //注意这里是小数,用fabs
}
return 0;
}
4.直线、折线分割平面
主要记住两个公式
/* 直线分割平面*/ 设有n条直线
围成的封闭的区域: M1= 1+2+3+....+n-2 ;
非封闭区域: M2= 2*n;
M = M1+M2
/*折线*/ 设有n 条
两条直线的一个端点相交,就变成了一条折线,被分割的区域减少2;
//n条折线分割的区域数M2,只需求出2n条直线分割区域数目为M, 则M-2*n=M2;
五、判断两个数是否相等
分析
考虑到数可能非常大,超过long long范围,double精度也可能满足不了时,我们采用字符串比较strcmp
函数
. 的ASCII码小于任何一个数字
思考
5.0和5==> 所以我们要去掉小数点后的0
方法一: 装入一个新数组
#include<stdio.h>
#include<string.h>
char a[100000],b[100000],c[100000],d[100000];
int i,k,len1,len2;
int main()
{
while(~scanf("%s %s",&a,&b))
{
len1=strlen(a);
len2=strlen(b);
k=len1;
for(i=len1-1;a[i]=='0';i--);
/*i是小数点后第一个0的位置,或者没有小数点,
第一个0的位置 5000, 5.000*/
if(strstr(a,".")==0)
//当输入的没有小数点时,会改变i的值, 不会改变数的大小
{
i=len1-1;
}
for(k=0;k<=i;k++)
c[k]=a[k]; //把第一个数去小数点后0后转到c中
if(c[k-1]=='.')
c[k-1]='\0';
else
c[k]='\0'; //设置数组的最后一位
k=len2;
for(i=len2-1;b[i]=='0';i--);
if(strstr(b,".")==0)
{
i=len2-1;
}
for(k=0;k<=i;k++)
d[k]=b[k];
if(d[k-1]=='.')
d[k-1]='\0';
else
d[k]='\0';
if(strcmp(c,d)==0)
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
//法二:去.0000
#include<stdio.h>
#include<string.h>
char a[100000],b[100000];
void change(char s[])
{
int len,i;
len=strlen(s);
if(strstr(s,".")) //如果它有小数点
{
for(i=len-1;s[i]=='0';i--) //在刨小数点后边的0
{
s[i]='\0'; //最后加个\0结束符号
len--;
}
}
if(s[len-1]=='.')
s[len-1]='\0';
}
int main()
{
while(~scanf("%s%s",&a,&b))
{
change(a);
change(b);
if(strcmp(a,b)==0)
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
六、等差数列的一个应用
问题描述 :给出一个序列 1–n, 求出可能的子序列,该子序列的和是m
如果写两层循环,会时间超限
#include <stdio.h>
#include <math.h>
long long n, m, a, i, t;
int main(void){
while(1){
scanf("%lld %lld", &n, &m);
if(n == 0 && m == 0){
return 0;
}
else{
t = sqrt(2 * m);//跟除法有关的,判断了前面的,比如100=10*10,这里是乘的关系,循环到那就可以了。
for(i = t; i >= 1; i--){
a = (m - i*(i+1) / 2) / i; //等差数列
if( a*i + i*(i+1)/ 2 == m){
printf("[%lld,%lld]\n", a+1, a+i); //注意是a+1,仔细看原理。
}
}
}
printf("\n");
}
return 0;
}