XUST——Kcsoftware Part3 题目题解

简介: 笔记

A - 阶乘后面0的数量


题解:

题目要求我们计算阶乘结果后0的个数,刚开始很多同学都是去尝试进行暴力求解,计算出最后结果再统计,毫无疑问这个是会爆掉的,所以我们要去思考新的办法去解决这个问题。


那么我们就要观察其中的本质,对于两个大数字相乘,都可以拆分成多个质数的相乘,而


此类问题很显然属于数学问题,一定要找到其中的本质规律才能得到正确的数学模型。


两个大数字相乘,都可以拆分成多个质数相乘,而质数相乘结果尾数为0的,只可能是2*5。如果想到了这一点,那么就可以进一步想到:两个数相乘尾数0的个数其实就是依赖于2和5因子的个数。又因为每两个连续数字就会有一个因子2,个数非常充足,所以此时只需要关心5因子的个数就行了。


然后我们就只需要去考虑如何找到n!中5因子的个数呢?


我们不妨举例子假设:


10!=10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1            可以看到10!中5的因子为10和5,有两个


15!=15 * 14 * 13 * 12 * 11 * 10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1             15!中5的因子是15、10和5,有三个


由此可见,n除以5便可得到5的因子sum。


但是,当5的因子不止含有一个5呢?例如25、125、625。


当5的因子含有2个5相乘时,25 = 5 * 5,我们需要将sum加上n除以5再除以5的个数,这时sum就包含将25分成两个5的因子之后的总个数。


当5的因子含有3个5相乘时,125 = 5 * 5 * 5,我们需要将sum加上n除以5再除以5再除以5的个数,这时sum就包含将125分成3个5的因子之后的总个数。


根据这个原理我们就可以解出这道题目了


AC


#include <stdio.h>
int main(void)
{
  long long N ;
  long long  x , m ;
  x = 0 ;
  scanf("%lld",&N);
  x=N/5;
  m=x;
  while(m!=0)
  {
  m=m/5;
  x+=m;
  }
  printf("%lld",x);
  return 0;
}

B - 上台阶 easy


对于该问题,适合用递归的方法来计算。从倒数第一步开始、从未到头思考,倒数第一步可能走了一级台阶,也可能走了两级。对于倒数第一步走一级的情况,可分类成倒数第二步走了一级与两级的情况;对于倒数第一步走了两级的情况,也可分类成倒数第二步走了一级与两级的情况……以此类推,见下图 :

50.png

AC


#include <stdio.h>
int arr(int n)
{
  n = n - 1 ;
  int a = 1 ;
  int b = 2 ;
  int c = n ;
  while (n > 2)
  {
  c = a + b ;
  a = b ;
  b = c ;
  n-- ;
  }
  return c ;
}
int main(void)
{
  int n  ;
  int sum ;
  scanf("%d",&n) ;
  sum = arr (  n ) ;
  printf("%d\n", sum ) ;
  return 0 ;
 } 
#include <iostream>
using namespace std;
int goup(int number){
  if(number == 1) return 1;
  else if(number == 2) return 1;
  else return goup(number - 1) + goup(number -2);
}
int main(){
  int n;
  cin >> n;
  cout << goup(n) << endl;
  return 0;
}

C - 查找


首先题目意思是给一串已经排好了的数字问你某个数字的位置。

如果从1到n扫一遍的话肯定超时。

所以这里用了二分的方法:


首先找到这串数字中间位置的那个数,然后与需要查询的数比较


如果要查询的数小于中间那个数,那么答案肯定在左边


如果要查询的数大于中间那个数,那么答案肯定在右边


如果等于的话继续在左边找,因为找到的位置还不能确定是第一个数


如此重复,直到要查询的区域变为1。


#include<iostream>
using namespace std ;
const int N = 1e6+10 ;
int a[N] ;
int n, m , s;
int main()
{
    cin >> n >> m ;
    for(int i=1; i<=n; i++)
    {
        cin >> a[i] ;
    }
    while(m--)
    {
        cin >> s ;
        int l=1, r=n ;
        while(l<r)
        {
            int mid = l+r >> 1 ;
            if(a[mid]>=s)   r=mid ;
            else    l=mid+1 ;
        }
        if(a[l]!=s) cout << "-1 " ;
        else
        {
            cout << l << " " ;
        }
    }   
    return 0 ;
}



D - Kevin and Permutation


题目翻译:


在他的生日,凯文收到了一组成对的数字1,2,3,…n作为礼物。

他将以一种方式排列这些数字,使得两个连续数字之间的最小绝对差尽可能大。更正式地说,如果他按照P1、P2、…、Pn的顺序排列数字,他希望最大化数值


其中|x|表示x的绝对值。

帮助凯文做到这一点。


题意描述:


给你一个数字 ww, 让你构造一个长度为 ww 的串,其中串元素由 1,2,3......w1,2,3......w 构成,且每个数字只出现一次,要求使得串相邻的两个元素两两之差的最小的绝对值最大51.png

#include<bits/stdc++.h>
using namespace std;
int main()
{
  int t;
  cin >> t;
  while(t--)
  {
  int n;
  cin >> n;
  int s = n/2;
  if(n%2 == 0)
  {
    for(int i=n, j=s; j>0; i--, j--)
    {
    cout << j <<  " " << i << " "; 
    }
  }
  else
  {
    cout << s+1 << " ";
    int i, j;
    for(i=n, j=s; j>0; i--, j--)
    {
    cout << i <<  " " << j << " "; 
    }
  }
  cout << endl;
  }
  return 0;
}

E - Technical Support


题目翻译:


你在一家大公司的技术支持质量控制部门工作。您的工作是确保所有客户问题都已解决。

今天,您需要查看客户和技术支持经理之间的对话副本。根据工作规则,客户机的每条消息后面必须有一条或几条消息,这是支持经理的答案。然而,有时客户问问题的速度太快,以至于在客户问了一些新问题之后,经理对旧问题的一些回答就会出现。

由于隐私政策,您无法获得消息的全文,只能看到消息的顺序以及每条消息的类型:客户问题或技术支持经理的答复。保证对话从客户的问题开始。

您必须确定此对话框是否符合上述工作规则,或者是否确实违反了这些规则。


输入

每个测试包含多个测试用例。第一行包含测试用例的数量t(1<500)。测试用例描述如下。

每个测试用例的第一行包含一个整数(1100)-对话框中的消息总数。

每个测试用例的第二行由n个字符“Q”和“A”组成,按时间顺序描述对话框中的消息类型。字符“”表示带有客户问题的消息,字符“A”表示带有技术支持经理答案的消息。保证行中的第一个字符等于“”

输出

对于每个测试用例,如果对话框可能对应于工作规则,则打印“是”(不带引号),否则打印“否”(不含引号)。


思路:


让我们从左到右处理字符串中的每个字符,并存储未回答问题的数量。最初,该值等于零。考虑字符串的第si个字符。如果等于“”,则将Scnts增加一。如果等于“A”,则减1。如果S变为负数,则意味着某些问题已被多次回答。在这种情况下,让我们为cntss指定零。

如果S在处理完所有字符串后都等于零,那么所有问题都得到了回答,并且答案是“是”。否则,答案是“否”。


AC


#include<bits/stdc++.h>
using namespace std;
bool flag = false;
int main()
{
  int t;
  cin >> t;
  while(t--)
  {
  flag = false;
  int k;
  cin >> k;  
  string n;
  cin >> n;
  int v = k-1;
  int s = 0;
  int m = 0;
  while(v >= 0)
  {
    if(n[k-1] == 'Q')
    {
    flag = true;
    break;
    }
    while(n[v] == 'A')
    {
    m++;
    v--;
    //  cout << v << endl;
    }
    while(n[v] == 'Q')
    {
    s++;
    v--;
    //  cout << v << endl;
    }
  //  cout << s << m;
    if(s>m)
    {
    flag = true;
    break;
    }
  }
  if(flag)
  {
    cout << "No" << endl;
  }
  else
  {
    cout << "Yes" << endl;
  }
  }
  return 0;
  }



F - Divisibility by 2^n



题意:


给定数列a1,a2,....an


令ans=a1*a2*a2*....an


每次可以选择一个i(只能选一次),使得ai=ai*i


若操作后存在(2^n)| ans,输出最小的操作次数,否则输出-1


解:


可以发现,ans的结果与操作是可以分离开的


如果(2^n)|ans,则ans的2的幂次>=n


在输入的时候即可处理ans的2的幂次,记为cnt


cnt>=n,不需要处理,输出0


cnt<n,需要处理出 n-cnt个2


考虑下标i 的贡献,显然我们需要先操作贡献最大的


注意到只有偶数有贡献,预处理下标2的贡献


不需要对i分解,利用dp的思想(状态保存),f(i)=f(i/2)+1,递推即可


#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int maxn=2e5+2e1;
array<ll,maxn>f;
ll div(ll x)
{
    ll cnt=0;
    while(x%2==0){cnt++;x/=2;}
    return cnt;
}
void init()
{
    f[2]=1;
    for(int i=4;i<maxn;i+=2)
    {
        f[i]=f[i/2]+1ll;
    }
}
void find_two(int n,ll cnt)
{
    vector<ll>a;
    for(int i=2;i<=n;i+=2)
    {
        a.emplace_back(f[i]);
    }
    sort(a.rbegin(),a.rend());
    ll k=0,cur=0;
    for(const auto&i:a)
    {
        cur+=i;k++;
        if(cur>=cnt){cout<<k<<"\n";return;}
    }
    cout<<"-1\n";
}
void solve()
{
    int n;cin>>n;
    ll ans=0;ll x;
    for(int i=0;i<n;i++)
    {
        cin>>x;
        ans+=div(x);
    }
    ll cnt=n-ans;
    if(cnt<=0)cout<<"0\n";
    else
    {
        find_two(n,cnt);
    }
}
int main()
{
    init();
    int t;cin>>t;
    while(t--)
    {
        solve();
    }
    return 0;
}
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
int a[N];
int main()
{
  int t;
  cin >> t;
  while(t--)
  {
  int n;
  cin >> n;
  int sum = 0;
  for(int i=1; i<=n; i++)
  {
    int k;
    cin >> k;
    while(k%2 == 0)
    {
    k/=2;
    sum++;
    }
    int j=i;
    a[i]=0;
    while(j%2 == 0)
    {
    j/=2;
    a[i]++;
    }
  }
  sort(a+1, a+1+n, greater<int>());
  int ans = 0;
  for(int i=1; i<=n && sum<n; i++)
  {
    sum+=a[i];
    ans++;
  }
  if(sum<n) cout << "-1" << endl;
  else  cout << ans << endl;
  }
  return 0;
}


G - 迷宫


这是一道经典的深度搜索题目 我们使用二维数据去存下该地图,如有障碍物则该数组位置标为1,经历过的路径我们将其标为2 如果没有障碍并且不是自己走过的,就进一步搜索,把自己走过的路打上标记1,返回时,再将标记还原;这样我们通过判断是否可以到达终点去寻找路径的个数。


AC


#include<bits/stdc++.h>
using namespace std;
const int N = 10;
int n, m ,t;
int sx, sy, fx, fy;
int vis[N][N];
int sum;
int nx[] = {0, 0, 1, -1};
int ny[] = {1, -1, 0, 0};
void dfs(int x, int y)
{
  vis[sx][sy] = 2;
  if((x == fx) && ( y == fy))
  {
  sum++;
  return;
  }
  for(int i=0; i<4; i++)
  {
  int tx = x+nx[i];
  int ty = y+ny[i];
  if(tx>=1 && tx<=n && ty<=m && ty>=1 && vis[tx][ty] == 0)
  {
    vis[tx][ty] = 2;
    dfs(tx, ty);
    vis[tx][ty] = 0;
  }
  }
}
int main()
{
  cin >> n >> m >> t;
  cin >> sx >> sy >> fx >> fy;
  while(t--)
  {
  int n, m;
  cin >> n >> m;
  vis[n][m] = 1;
  }
  dfs(sx, sy);
  cout << sum << endl;
  return 0;
}


H - Minimize the Thickness


题意:给一个数列,把它切成若干段,要求每段包含元素的和相等,


给出定义:thickness,即切成若干段后包含最多元素的那一段的元素数量。


求怎么切使thickness最小,输出最小的thickness


思路:此题目需要多次求数列某i ~  j项的和,为了简化代码便于实现,把数列每一项元素替换为他的前缀和(前缀和就是从某项开始,之前所有项的和)。


eg:  数列  {a1 a2 a3 a4}  替换为{S1 S2 S3 S4}  


如果用枚举,我们只需要枚举切前k项作为第一段,此时段的元素的和便确定,之后只需要遍历第一段后的数组元素,判断这么切能不能使最后所有段的和相等,若符合,且该种分法的thickness小于前一种分法,则更新答案。时间复杂度:套两层for,约为O(n^2),TL:2s , 数列项数小于2000,不会超时。


#include<iostream>
using namespace std;
const int N = 2020;
int n, m;
int a[N];
int goo(int i, int sum)
{
  if(i == m)  return 0;
  for(int j=i+1, cur=0; j<=m; j++)
  {
  cur += a[j-1];
  if(cur>sum) return m;
  if(cur==sum) return max(j-i, goo(j, sum));
  }
  return m;
}
int solve()
{
  int ans = m;
  for(int i=0, sur=0; i<m-1; i++)
  {
  sur += a[i];
  ans = min(ans, goo(0, sur));
  }
  return ans;
}
int main()
{
  cin >> n;
  while(n--)
  {
  cin >> m;
  for(int i=0; i<m; i++)  cin >> a[i];
  cout << solve() << endl;
  }
  return 0;
}


I - Scuza


题意:有n 个台阶的高度,和k个腿长,求每个腿长能走最长多少米的台阶


思路:前缀最大值+前缀和+二分


现将前缀台阶高度和前缀最大值求出(即到达i位置需要的最大腿长),然后每次在前缀最大值数组里找第j个腿长,找到的位置输出该位置的前缀和,即该腿长能走的台阶最高高度


AC:

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
int main()
{
  int t;
  cin >> t;
  while(t--)
  {
  int n, m;
  cin >> n >> m;
  vector<long long> pref;
  vector<int> pref_max;
  pref.push_back(0);
  for(int i=0; i<n; i++)
  {
    int x;
    cin >> x;
    pref.push_back(x+pref.back());
    if(i==0)  pref_max.push_back(x);
    else
    {
    pref_max.push_back(max(pref_max.back(), x));
    }
  }
  for(int i=0; i<m; i++)
  {
    int k;
    cin >> k;
    int ans = upper_bound(pref_max.begin(), pref_max.end(), k)-pref_max.begin();
    cout << pref[ans] << " ";
  }
  cout << endl;
  }
}
相关文章
|
12月前
|
存储 缓存 算法
LeetCode刷题---Two Sum(一)
LeetCode刷题---Two Sum(一)
|
4月前
|
存储 搜索推荐
题目----力扣--合并两个有序数组
题目----力扣--合并两个有序数组
28 0
|
4月前
题目----力扣--反转链表
题目----力扣--反转链表
27 0
|
12月前
【力扣-TS解题】1、回文数
【力扣-TS解题】1、回文数
42 0
|
4月前
|
算法 Java
LeetCode算法题---两数之和(一)
LeetCode算法题---两数之和(一)
46 0
|
4月前
|
算法 机器人
class051 二分答案法与相关题目【算法】
class051 二分答案法与相关题目【算法】
52 0
|
4月前
力扣每日一道系列 --- LeetCode 88. 合并两个有序数组
力扣每日一道系列 --- LeetCode 88. 合并两个有序数组
47 0
|
算法
LeetCode每日1题--螺旋矩阵
LeetCode每日1题--螺旋矩阵
64 0
LeetCode每日1题--螺旋矩阵
力扣---LeetCode88. 合并两个有序数组
第七弹——力扣LeetCode每日一题
|
算法
LeetCode每日1题--四数之和
LeetCode每日1题--四数之和
72 0