HNUCM-OJ 2022年寒假训练赛(一)

A:十六进制回文数

题目描述

输出在十进制数m~n(1<=m<n<=1000000)之间的非十进制回文且为十六进制回文的数字个数。

回文数:正序倒序写法相同的数。

解题思路

将数字存在数组中,从头和尾开始用两个变量记录位置进行比较。当奇数位数字为回文数时两变量指向同一个位置(中间位置);当偶数位数字为回文数时两变量指向相邻两个位置。

示例代码
#include <stdio.h>
#include <string.h>

int main(void)
{
    int m,n,i,j,k,a,b,count;			//m n:输入;count:输出;i j k a b:用于辅助计数
    int dec[8],hex[8];					//dec:十进制数;hex:十六进制数
    while(~scanf("%d%d",&m,&n))
    {
        memset(hex,0,sizeof(hex));
        memset(dec,0,sizeof(dec));
        for(i = m,count = 0;i <= n;i++)
        {
            for(j = 0,dec[0] = i;j < 8;j++)		//将十进制数存入数组dec
            {
                if(dec[j] > 9)
                {
                    dec[j+1] = dec[j]/10;
                    dec[j] = dec[j]%10;
                } else
                {
                    break;
                }
            }
            if(i >= 10)
            {
                for(k = 0;dec[k] == dec[j] && j > k;k++,j--);
            }
            if(j != 0 && j > k)					//当前十进制数不止一位数且不相同位置上的数字不相同,即该十进制数不是回文数
            {
                for(a = 0,hex[0] = i;a < 8;a++)
                {
                    if(hex[a] > 15)
                    {
                        hex[a+1] = hex[a]/16;
                        hex[a] = hex[a]%16;
                    } else
                    {
                        break;
                    }
                }
                b = 0;
                if(i >= 16)
                {
                    for(b = 0;hex[a] == hex[b] && a > b;b++,a--);
                }
                if(i < 16 || a <= b)			//当前十六进制数仅一位或不同位置上的数字相同,则表明当前十六进制数为回文数
                {
                    count++;
                }
            }
        }
        printf("%d\n",count);
    }
    return 0;
}
小结

回文数的写法分两步:进制转换并将转换结果存入数组(方便比较);判断数字性质(是否为回文数)。

进制转换与高精度数字存储原理相同。只需将余数存入低位,进位存入高位即可。

判断数字是否为回文数的条件是数字从头尾两端开始比较的数字都相同。示例代码中的操作是用两个变量分别指向头和尾两端的位置,当指向同一个位置或两变量的相对位置发生变化则表明该数为回文数。

B:lsx的立方体

题目描述

求边长为n的立方体空间内可构成的等边三角形的数目。等边三角形每边必须平行于oxy、oxz、oyz的某个表面,每个点只能在立方体的边界或内点上,每个点的三个坐标x、y、z必须是整数。

解题思路

在三维坐标系只能取整数作为坐标的情况下,每个立方体都可以找到两组顶点,使得每一组中的任意两点连接所得直线与另两点连接所得直线异面垂直。故一个立方体中能找出8个坐标均为整数的等边三角形。

根据题意,寻找边长不超过n的立方体个数乘8即可。

示例代码
#include <stdio.h>
#include <string.h>

int main(void)
{
    int n,sum,i;
    while(~scanf("%d",&n))
    {
        for(i = 1,sum = 0;i <= n;i++)
        {
            sum += pow(n-i+1,3);
        }
        printf("%d\n",sum*8);
    }
    return 0;
}
小结

本题仅考虑整数情况。重点是理解清楚题意。

C:X星切糕

题目描述

将长度为n的切糕分成单位长度为1~5的小段,输出所能得到的最高价值。单位长度/价值表如下。

单位长度价值
13
27
311
415
520
解题思路

动态规划->完全背包问题。

在0-1背包问题的基础上进行改进,即从“放该物品0-1次取最大值”变为从“放该物品0-n次取较大值”,其中n为当前背包容量能装载该物品的最大个数。

本题中切糕长度对应背包容量,单位长度对应物品重量。

示例代码
#include <stdio.h>
#include <string.h>
 
int main(void)
{
    int n,i,j;							//n:输入;i j:辅助计数
    int value[6] = {0,3,7,11,15,20};	//存放价值,意义为“长度为i的切糕价值为value[i]”
    int dp[1000005];					//用于存放最大价值
    while(~scanf("%d",&n))
    {
        memset(dp,0,sizeof(dp));
        for(i = 1;i <= 5;i++)
        {
            for(j = i;j <= n;j++)
            {
                dp[j] = (dp[j]>(dp[j-i]+value[i]))?dp[j]:(dp[j-i]+value[i]);
            }
        }
        printf("%d\n",dp[n]);
    }
    return 0;
}
小结

完全背包问题的状态方程:
D P [ i ] [ j ] = M a x ( D P [ i − 1 ] [ j ] , D P [ i ] [ j − W e i g h t [ i ] ] + V a l u e [ i ] ) DP[i][j] = Max(DP[i-1][j] , DP[i][j-Weight[i]]+Value[i]) DP[i][j]=Max(DP[i1][j],DP[i][jWeight[i]]+Value[i])
式中,数组Weight表示物品重量,数组Value表示物品价值。

示例代码中将二维数组简化为一位数组,即在不(再次)装入该物品和(再次)装入该物品中选取较大值。该操作仅参考本行数据,故可将二维数组压缩为滚动的一维数组以减小空间开销。

D:解密

题目描述

输出一个字符串中完全独立(不交叉不重叠)的子串的长度。

解题思路

动态规划->最长公共子串的变形。需要加上的限制条件就是保证子串相互独立。

示例代码
#include <stdio.h>
#include <string.h>
#include <math.h>
int main(void)
{
    int i,j,max;				//i j:辅助计数;max:最长公共子串长度计数器
    char str[1005];				//输入的原字符串
    int dp[1005][1005];			//计算公共子串长度的数组
    while(~scanf("%s",str))
    {
        memset(dp,0,sizeof(dp));
        for(i = 1,max = 1;i <= strlen(str);i++)
        {
            for(j = 1;j <= strlen(str);j++)
            {
                if(str[i-1] == str[j-1] && i != j)
                {
                    dp[i][j] = dp[i-1][j-1]+1;
                    if(dp[i][j] > max && max < abs(i-j))		//限制公共子串长度,使得两子串不重叠
                    {
                        max = dp[i][j];
                    }
                }
            }
        }
        printf("%d\n",max);
    }
    return 0;
}
小结

对于独立的两条公共子串来说,子串内同一位置的字符在原字符串中的相对位置的长度是固定的。

eg.字符串ABCDABC的独立公共子串D之前的ABCD之后的ABC,无论是ABC在原字符串中的相对位置都相差4。

利用上述条件即可限制公共子串的长度保证其相互独立。

E:第K大数

题目描述

输入n个数,找出第k大的数。多次出现的相同数字仅计算一次。若不存在第k大的数则输出-1。

解题思路

用数组存储输入的数字,数组的值表示该数字出现的次数。若该数出现次数不为0则为序列中的数字。倒序输出第k个数即可。

示例代码
#include <stdio.h>
#include <string.h>

int main(void)
{
    int n,k,max,i,j;			//n k:输入;max:最大的数和第k大的数;i j:辅助计数
    int count[1005];			//用于存储数字出现的次数,count[i]意为数字共出现了count[i]次
    while(~scanf("%d%d",&n,&k))
    {
        memset(count,0,sizeof(count));
        for(i = 0,max = -1;i < n;i++)
        {
            scanf("%d",&j);
            count[j]++;
            if(j > max)
            {
                max = j;
            }
        }
        for(i = 0;max >= 0;max--)
        {
            if(count[max] != 0)
            {
                i++;
            }
            if(i == k)
            {
                break;
            }
        }
        printf("%d\n",max);
    }
    return 0;
}
小结

按照题目要求,max初值应当赋为-1。

F:字符串变换

题目描述

(1) 原始字符串的总长度为n,每次从左到右取长度为m(2 <= m <= n)的子字符串进行变换;如果剩余字符串长度不到m,则取剩余全部字符串进行变换。
(2) 每次取长度为m的子字符串进行变换时需要将子串中的小写字母全部改为大写字母,然后反转该子串。

解题思路

长度为n的字符串按照m长度划分为一个单位,将每个单位内的字符全部改为大写并倒序输出该单位内的字符,剩余不足一个单位数量的字符自动划分为一个单位处理。

示例代码
#include <stdio.h>
#include <string.h>

int main(void)
{
    int n,m,i,j,set;				//n m:输入;i j:辅助计数;set:单位计数器
    char t;							//t:用于辅助置换变量
    char str[1005];					//输入字符串
    while(~scanf("%d%d",&n,&m))
    {
        scanf("%s",str);
        for(set = 0;set*m < n && (set+1)*m < n;set++)	//处理完整单位
        {
            for(i = set*m;i < (set+1)*m;i++)
            {
                if(str[i] >= 'a' && str[i] <= 'z')
                {
                    str[i] -= 32;
                }
            }
            for(i = set*m,j = (set+1)*m-1;j >= i;j--,i++)
            {
                t = str[i];
                str[i] = str[j];
                str[j] = t;
            }
        }
        for(i = set*m;i < n;i++)						//处理不完全单位
        {
            if(str[i] >= 'a' && str[i] <= 'z')
            {
                str[i] -= 32;
            }
        }
        for(i = set*m,j = n-1;j >= i;j--,i++)
        {
            t = str[i];
            str[i] = str[j];
            str[j] = t;
        }
        printf("%s\n",str);
    }
    return 0;
}
小结

对于完整单位和不完整单位需要分开处理。

G:评分计算器

题目描述

输入商家得到1~5星评分的个数,计算评分平均数。若输入全为0,则输出0.0。

解题思路

评分平均数 = 总评分 * 总评分个数。

其中总评分指1~5星所有评分。

示例代码
#include <stdio.h>
#include <string.h>

int main(void)
{
    int i,sum;							//i:用于计数;sum:总评分个数
    double score;						//总评分和平均分
    int num[5];							//获得的1~5星评分的个数
    while(~scanf("%d",&num[0]))
    {
        score = 0;
        for(i = 1,sum = num[0];i < 5;i++)
        {
            scanf("%d",&num[i]);
            sum += num[i];
        }
        for(i = 0;i < 5;i++)
        {
            score += num[i]*(i+1);
        }
        if(sum != 0)									//当总评分不为0时,计算平均分
        {
            score /= sum;
        }
        printf("%.1lf\n",(int)(score*10)/10.0f);		//保留小数点后一位输出
    }
    return 0;
}
小结

注意精度问题。

保留小数点后x位(不四舍五入):

double n;
n = (int)(n * pow(10,x)) / pow(10,x);

四舍五入到小数点后x位:

double n;
n = (int)(n * pow(10,x) + 0.5) / pow(10,x);

H:缩页

题目描述

将输入的零散的页码数标识的范围输出。输入数据用逗号隔开。

解题思路

读取输入字符串的数字将其存放在另一数组中,排序,按题目格式顺序输出即可。

示例代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void QuickSort(int *arr, int low, int high)
{
    if (low < high)
    {
        int i = low;
        int j = high;
        int k = arr[low];
        while (i < j)
        {
            while(i < j && arr[j] >= k)
            {
                j--;
            }
            if(i < j)
            {
                arr[i++] = arr[j];
            }
            while(i < j && arr[i] < k)
            {
                i++;
            }
            if(i < j)
            {
                arr[j--] = arr[i];
            }
        }
        arr[i] = k;
        QuickSort(arr, low, i - 1);
        QuickSort(arr, i + 1, high);
    }
}

int main(void)
{
    int i,j,max,a,b;				//i j:辅助计数;max:记录数字个数;a b:用于输出;
    int count[100005];				//用于存放输入的字符串转化的数字
    char str[200005];				//用于存放输入的字符串
    while(gets(str))
    {
        memset(count,0,sizeof(count));
        for(i = 0,max = 0;i < strlen(str);i++)
        {
            while(i < strlen(str) && str[i] >= '0' && str[i] <= '9')
            {
                count[max] = count[max]*10 + (str[i]-'0');
                i++;
            }
            max++;
        }
        QuickSort(count,0,max-1);					//排序
        for(i = 0,a = 0,b = -1;i < max;i++)			//输出
        {
            if(i == 0 || count[i-1] < count[i]-1)
            {
                a = count[i];
            }
            if(i == max-1 || count[i+1] > count[i]+1)
            {
                b = count[i];
            }
            if(a <= b)
            {
                if(a == b && i != max-1)
                {
                    printf("%d,",a);
                }
                if(a == b && i == max-1)
                {
                    printf("%d\n",a);
                }
                if(a < b && i != max-1)
                {
                    printf("%d-%d,",a,b);
                }
                if(a < b && i == max-1)
                {
                    printf("%d-%d\n",a,b);
                }
                a = 0;
                b = -1;
            }
        }
    }
    return 0;
}
小结
  • 注意数组范围。
  • 如需排序尽量使用快排。
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值