首页 > 编程语言 >2018算法课习题(一)

2018算法课习题(一)

时间:2023-05-29 11:33:03浏览次数:66  
标签:数字 int 样例 金币 算法 2018 提交 习题 输入


目录:

数字统计问题

2011的倍数

最多约数问题

最大间隙问题

字典序问题

金币列阵问题

更新中......

问题 B: 数字统计问题(二)

时间限制: 1 Sec  内存限制: 128 MB
提交: 8  解决: 6
[提交] [状态] [讨论版] [命题人:admin]

题目描述

给定一本书,其中包含n页,计算出书的全部页码中用到了多少个数字0…9?页码从1开始

输入

一个整数n,代表页码总数。(0<=n<=109)

输出

十行,每行一个整数,分别表示0~9每个数字出现的次数

样例输入

11

样例输出

1
4
1
1
1
1
1
1
1
1

提示

注意!答案范围可能超过int存储范围。

[提交][状态]

【分析】

1. 暴力思路。枚举从1到n每一个数字,对于i,分解这个数字每一位,数一数即可。

2. 组合数学计数。考虑每一位放数字0~9,存在的数字数量。

比如n=12345,在中间那一个3的位置,

如果放1(小于3),那么种数=(12+1)*100,高位00~12,低位00~99

如果放3(等于3),那么种数=12*100+45+1,高位00~11,低位00~99,加上,高位12,低位00~45

如果放8(大于3),那么种数=12*100, 高位00~11,低位00~99

对于每一位,枚举放0~9,统计数量即可。

特别注意,上面的统计方法会统计前导0,最后要减掉带前导0的数量

【代码】

#include<stdio.h>
typedef long long ll;
ll cnt[10]={0};
ll bit[12];
int main()
{
    int n,top=0;
    scanf("%d",&n);
    for(int i=0;i<12;i++) //预处理10的i次幂
        bit[i]=(i==0?1:bit[i-1]*10);
    for(int i=n;i;i/=10) //数一数数字n的长度
        ++top;
    for(int i=top-1;i>=0;i--) //从高位向低位遍历
    {
        int pre=n/bit[i+1]; //获取高位数字
        int up=n/bit[i]%10; //获取当前数字
        int low=n%bit[i]; //获取低位数字
        for(int j=0;j<10;j++) //枚举当前位放0~9
        {
            if(j<up) cnt[j]+=(pre+1)*bit[i]; //随意放
            else if(j==up)cnt[j]+=pre*bit[i]+low+1; //高位小 + 高位相等时低位的数量
            else if(j>up) cnt[j]+=pre*bit[i]; //比当前位时,高位只能小于pre
        }
    }
    for(int i=top-1;i>=0;i--) //由于上面枚举0时,0会作为前导0,因此减掉个位前导0的次数
        cnt[0]-=bit[i];
    for(int i=0;i<10;i++)
        printf("%lld\n",cnt[i]);
    return 0;
}

问题 D: 2011的倍数(二)

时间限制: 1 Sec  内存限制: 128 MB
提交: 9  解决: 6
[提交] [状态] [讨论版] [命题人:admin]

 

题目描述

给定一个正整数n,多少个1组成的整数可以被n整除?

输入

一个整数n(1<=n<=10^5) 

输出

一个正整数,表示数字1的个数。如果无解,请输出-1

样例输入

2011

样例输出

670

[提交][状态]

【分析】

同余模定理:(a+b)%n=(a%n+b%n)%n;   a*b%n=a%n*b%n

一般思路,暴力乘10再加1,直到模除2011等于0。但是数字太大时,C语言不提供大数存储。

令res表示答案,初始为1。状态转移:res=res*10+1,我们要求的是res%n的值,因此状态转移后,让res模除n,对结果是无影响的。

如何判断无解:用vis数组标记每个数字对模除n的余数是否出现过,如果出现过,再次碰到,说明形成了循环,永远找不到模除n等于0的数。

【代码】

#include<stdio.h>
bool vis[200000];
int main()
{
    int n,p=1,ans=1;
    scanf("%d",&n);
    for(int i=0;i<200000;i++)
        vis[i]=false;
    while(p%n!=0)
    {
        if(vis[p])
        {
            ans=-1;
            break;
        }
        vis[p]=true;
        p=(p*10+1)%n;
        ans++;
    }
    if(ans==-1)
        printf("-1\n");
    else
        printf("%d\n",ans);
    return 0;
}

 

问题 E: 最多约数问题

时间限制: 1 Sec  内存限制: 128 MB
提交: 19  解决: 8
[提交] [状态] [讨论版] [命题人:admin]

 

题目描述

正整数 x 的约数是能整除x的正整数,其约数的个数记为div(x),例如div(10)=4。设 a 和 b 是两个正整数,找出 a 和 b 之间(包含a,b)约数个数最多的数 x 的约数个数

输入

两个正整数a和b,(1<=a<=b<=105)

输出

一个正整数表示答案。

样例输入

1 36

样例输出

9

[提交][状态]

【分析】

对于一个数字x,求其因子个数,一般思路,循环1到n,只要模除等于0,即为因子,时间复杂度O(n),优化思路:循环到1到sqrt(n),因为对于x的因子a,若a<sqrt(n),则必然存在b>sqrt(n),使得a*b=n。若sqrt(n)恰好是个整数,也将是一个因子。时间复杂度O(sqrt(n))

循环a到b,找出最大因子数即可。总时间复杂度O(n*sqrt(n))

【代码】

#include<bits/stdc++.h>
using namespace std;
int get(int x)
{
    int res=0;
    for(int i=1;i*i<=x;i++)
    {
        if(x%i==0)res+=2;
        if(i*i==x)res--;
    }
    return res;
}
int main()
{
    int a,b,ans=0;
    scanf("%d%d",&a,&b);
    for(int i=a;i<=b;i++)
    {
        ans=max(ans,get(i));
    }
    printf("%d\n",ans);
}

问题 F: 最大间隙问题

时间限制: 9 Sec  内存限制: 1024 MB
提交: 34  解决: 7
[提交] [状态] [讨论版] [命题人:admin]

 

题目描述

给定 n 个实数,求这n个实数在数轴上相邻2个数之间的最大差值,设计解最大间隙问题的线性时间算法(时间复杂度为O(n))。

输入

第一行一个正整数n(2<=n<=2×107)
第二行n个实数,数据保证这些实数只有一位小数。

输出

一个实数,保留1位小数。

样例输入

5
2.3  3.1  7.5  1.5  6.3

样例输出

3.2

[提交][状态]

【分析】

鸽笼原理:将n个鸽子放到n+1个笼子里,不管怎么放,必然至少有一个笼子是空的。

对于输入的n个数字,找到最大值最小值,还剩下n-2个数字,将其放到n-1个区间里,则必然有空区间。

最大间隙一定出现在空区间附近。有可能多个空区间连在一起。

分块n-1个区间,每个区间存储区间内的最大值和最小值。

扫描放好的数轴,记录下最大的空隙即可。总时间复杂度O(3n)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int N=2e7+5;
double Max[N],Min[N],a[N];
int n;
int main()
{
    double mx=-1e16,mn=1e16;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%lf",&a[i]);
        mx=max(mx,a[i]);
        mn=min(mn,a[i]);
        Max[i]=-1e16;
        Min[i]=1e16;
    }
    if(mx==mn)
    {
        printf("0.0\n");
        return 0;
    }
    double len=(mx-mn)/(n-1); //分成n-1段,每段长度len
    for(int i=0;i<n;i++) //把n个数放进去
    {
        int pos=(a[i]-mn)/len;
        Max[pos]=max(Max[pos],a[i]);
        Min[pos]=min(Min[pos],a[i]);
    }
    double ans=0,last=mn;
    for(int i=0;i<n;i++)if(Min[i]<1e15)
    {
        ans=max(ans,Min[i]-last);
        last=Max[i];
    }
    printf("%.1f\n",ans);
}

问题 G: 字典序问题

时间限制: 1 Sec  内存限制: 128 MB
提交: 19  解决: 7
[提交] [状态] [讨论版] [命题人:admin]

 

题目描述

在数据加密和数据压缩中常需要对特殊的字符串进行编码。给定的字母表A由26个小写字母组成。该字母表产生的升序字符串中字母从左到右出现的次序与字母在字母表中出现的次序相同,且每个字符最多出现1次。例如,a,b,ab,bc,xyz等字符串都是升序字符串。现在对字母表中产生的所有长度不超过6的升序字符串,计算它在字典中的编码。

1

2

3

ab

ac

a

b

c

 

27

28

输入

第一行一个整数T,表示测试组数。
接下来T行,每行一个长度不超过6的升序字符串(仅含小写字母)。

输出

输出T行,每行一个整数代表答案。

样例输入

2
a
b

样例输出

1
2

[提交][状态]

【分析】

注意是升序字符串。

可以先预处理一个数组 dp[i][j],用它来表示 以字母 i 开头,长度为 j 的升序字符串有多少个。

求法:dp[i][0]=0, dp[i][1]=1是显然的,j 大于2时,dp[i][j]= dp[i+1][j-1]+dp[i+2][j-1]+...+dp['z'][j-1];意思是将 i 作为首字母时,后面可以安排 j-1 个字母,且必须以大于 i 的字母开头。

设输入字符串为str,接下来我们数一数有多少串会出现在str前面即可。

可以发现,长度小于str的合法串都在str前面,直接累加一下总数即可,长度等于str的串,需要考虑每一个位置放什么。第一个位置放小于str[0]的字符,或放等于str[0]的字符,需考虑第二位,同样第二位可以放小于str[1]的字符,或等于str[2]的字符并继续考虑第三位。。。。

【代码】

#include<bits/stdc++.h>
typedef long long ll;
ll dp[26][10];
int main()
{
    for(int i=25;i>=0;i--)
    {
        dp[i][1]=1;
        for(int j=2;j<10;j++)
        {
            dp[i][j]=0;
            for(int k=i+1;k<26;k++)
            {
                dp[i][j]+=dp[k][j-1];
            }
        }
    }
    int T;
    char s[10];
    scanf("%d",&T);
    while(T--)
    {
        scanf("%s",s);
        int n=strlen(s);
        ll ans=1;
        for(int i=0;i<26;i++)
            for(int j=1;j<n;j++)
                ans+=dp[i][j];
        for(int i=0;i<n;i++)
        {
            int j=0;
            if(i>0)j=s[i-1]-'a'+1;
            for(;j<s[i]-'a';j++)
                ans+=dp[j][n-i];
        }
        printf("%lld\n",ans);
    }
}

问题 H: 金币阵列问题

时间限制: 1 Sec  内存限制: 128 MB
提交: 4  解决: 2
[提交] [状态] [讨论版] [命题人:manager]

题目描述

m×n(m<=100,n<= 100)个金币在桌面上排成一个m列的金币阵列。每一枚金币或正面朝上或背面朝上。用数字表示金币状态,0表示金币正面朝上,1 表示背面朝上。金币阵列游戏的规则是:

(1)每次可将任一行金币翻过来放在原来的位置上;

(2)每次可任选2 列,交换这2 列金币的位置。
给定金币阵列的初始状态和目标状态,编程计算按金币游戏规则,将金币阵列从初始状态变换到目标状态所需的最少变换次数。

输入

输入的第1行有1个正整数k,表示有k组数据。每组数据的第1行有2个正整数m和n。以下的m行是金币阵列的初始状态,每行有n 个数字表示该行金币的状态,0 表示金币正面朝上,1 表示背面朝上。接着的m行是金币阵列的目标状态。

输出

按照输入数据的次序输出计算出的最少变换次数(规则1和2使用的次数之和),相应数据无解时输出-1。

样例输入


4 3 1 0 1 0 0 0 1 1 0 1 0 1 1 0 1 1 1 1 0 1 1 1 0 1


样例输出


2


[提交][状态]

【分析】

考虑第一列,其他任意一列都有可能作为第一列而获得最少变换次数,而想要去第一列,就必须变成和目标矩阵第一列相同。

这样就遍历到了所有情况。当第一列定下来后,第一列不符的位置所在行都要进行反转,然后第2~m列就只能进行列列交换了,扫描一下,遇到不行的列,就在后面找个行的换过来。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;

int src[120][120]; //原矩阵
int temp[120][120];
int aim[120][120]; //目标矩阵
int n,m; //行列

void swapCol(int u,int v) //对temp数组交换两列
{
    for(int i=1;i<=n;i++)
        swap(temp[i][u],temp[i][v]);
}

void changeRow(int x) //对temp数组翻转第x行
{
    for(int j=1;j<=m;j++)
        temp[x][j]^=1;
}

int checkCol(int x,int y) //检查temp第k列和aim的第p列是否相同
{
    for(int i=1;i<=n;i++)
        if(temp[i][x]!=aim[i][y])
            return 0;
    return 1;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
            scanf("%d",&src[i][j]);
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
            scanf("%d",&aim[i][j]);
    }

    int ans=INF;
    for(int j=1;j<=m;j++) //枚举将第j列作为第一列
    {
        memcpy(temp,src,sizeof(src)); //把src复制到temp
        int res=0;
        if(j!=1)//交换两列
        {
            swapCol(1,j);
            res++;
        }
        for(int i=1;i<=n;i++)
        {
            if(temp[i][1]!=aim[i][1]) //翻转第i行
            {
                changeRow(i);
                res++;
            }
        }

        for(int k=2;k<=m;k++) //考虑调整后面的列
        {
            if(!checkCol(k,k))
            {
                int p=k+1;
                while(p<=m&&!checkCol(p,k))p++; //找到后面可以放在k处的拿过来。
                if(p>m)
                {
                    res=-1; //没有找到可以放在k列的列,因此这时无解
                    break;
                }
                else
                {
                    swapCol(k,p);
                    res++;
                }
            }
        }
        if(res!=-1&&res<ans)
            ans=res;
    }
    if(ans==INF)ans=-1;
    printf("%d\n",ans);
}

 

标签:数字,int,样例,金币,算法,2018,提交,习题,输入
From: https://blog.51cto.com/u_16125110/6369255

相关文章

  • 2018ACM浙江省赛 ZOJ 4029 Now Loading!!!(二分)
    NowLoading!!!TimeLimit: 1Second     MemoryLimit: 131072KBDreamGridhas  integers .DreamGridalsohas foragivennumber ,where , .InputTherearemultipletestcases.Thefirstlineofinputisaninteger Thefirstlinecon......
  • 【论文解读|GL-Cache 】基于组级学习的缓存替换算法
    论文原文:GL-Cache:Group-levellearningforefficientandhigh-performancecaching|FAST'23源码地址:https://github.com/Thesys-lab/fast23-GLCache论文贡献:提出Group-levelLearning,利用多对象组的特征来适应工作负荷和缓存大小,通过分组来积累更强的学习信号,学......
  • 地理信息系统算法基础
    作者:张宏温永宁流爱利等第1章算法设计和分析第2章GIS算法的计算几何基础第3章空间数据的变换算法第4章空间数据转换算法第5章空间数据组织算法第6章空间度量算法第7章空间数据索引算法第8章空间数据内插算法第9章Delaunay三角网与Voronoi图算法第10章缓冲......
  • 算法刷题记录:珂朵莉的假toptree
    题目链接https://ac.nowcoder.com/acm/contest/19306/1035题目分析将每个数每一位都进行拆分即可。AC代码#include<iostream>usingnamespacestd;intn,p=1,num=1;inta[1005];intmain(){cin>>n;while(p<=1000){if(num>=1......
  • 数据结构与算法
    @目录数据结构与算法图解:算法——排序普通(简单)排序冒泡算法选择排序插入排序希尔排序堆排序递归排序归并排序快速排序分布式排序计数排序桶排序基数排序算法——搜索顺序搜索(线性搜索)有序搜索二分搜索插值搜索斐波那契搜索索引搜索(分块搜索)树搜索哈希搜索算法——随机算法算法设......
  • 算法学习-二分查找
    题目:C.PlaceforaSelfieCodeforcesRound862(Div.2)题目链接:Problem-C-Codeforces题目描述: 有若干抛物线(抛物线方程为a*x2+b*x+c,每条抛物线的a,b,c值给出)和经过原点,斜率不同的直线(斜率值k给出)。对于每条抛物线找出任意一条直线,它与该抛物线不相交。题目......
  • 期望最大化EM算法(2)
    一般形式的EM算法  期望最大化算法或者EM算法是,求解具有潜在变量的概率模型的最大似然解的一种通用方法。这里给出一般形式的EM算法,并启发式地推导EM算法最大化了似然函数。  考虑一个概率模型,将其中所有的观测变量联合起来记为\(X\),将所有的与观测变量对应的潜在变量记为......
  • 文心一言 VS 讯飞星火 VS chatgpt (24)-- 算法导论4.2 6题
    六、用Strassen算法作为子进程来进行一个knn矩阵和一个nkn矩阵相乘,最快需要花费多长时间?对两个输入矩阵规模互换的情况,回答相同的问题。文心一言:Strassen算法是一种用于矩阵乘法的分治算法,它将原始的矩阵分解为较小的子矩阵,然后使用子矩阵相乘的结果来计算原始矩阵的乘积......
  • 文心一言 VS 讯飞星火 VS chatgpt (24)-- 算法导论4.2 6题
    六、用Strassen算法作为子进程来进行一个knn矩阵和一个nkn矩阵相乘,最快需要花费多长时间?对两个输入矩阵规模互换的情况,回答相同的问题。文心一言:Strassen算法是一种用于矩阵乘法的分治算法,它将原始的矩阵分解为较小的子矩阵,然后使用子矩阵相乘的结果来计算原始矩阵的乘积。对......
  • 算法刷题记录:乒乓球
    题目链接https://ac.nowcoder.com/acm/contest/19306/1033题目分析这题好坑,乒乓球的比分如果相差<2,还得继续比下去,但是题目里面没有说qwq。看了眼题解才发现这个点。AC代码#include<iostream>usingnamespacestd;//统计11分制和21分制的比分strings;intmain(......