计算机保研机试准备——C++算法题(二)

第一篇:基础题

计算机保研机试准备——C++算法题(一)_计算机保研机试题单-CSDN博客文章浏览阅读643次,点赞4次,收藏4次。记录C++算法刷题,准备机试_计算机保研机试题单 https://blog.csdn.net/weixin_47520540/article/details/147255264?sharetype=blogdetail&sharerId=147255264&sharerefer=PC&sharesource=weixin_47520540&spm=1011.2480.3001.8118

四、函数递归题

(15)P4994 终于结束的起点

题目概括:

#include<bits/stdc++.h>
using namespace std;
int main(){
  int m;
  cin >> m;

  int a = 0, b = 1, c;
  int i = 0;
  while(true){
    c = (a+b)%m;
    a = b;
    b = c;
    i++;
    if(a==0&&b==1)break ;
  }
  cout << i;
  return  0;

}
/*
#include<bits/stdc++.h>
using namespace std;

int f[10000001];//尽可能大
int main() {
	int m;
	cin >> m;
	f[0] = 0;
	f[1] = 1;
	for (int i = 2; i < 10000001; i++) {
		f[i] = (f[i - 1] + f[i - 2]) % m;
	}
	int res = 0;
	for (int i = 1; i < 10000000; i++) {
		if (f[i] == 0 && f[i + 1] == 1) {
			res = i;
			break;
		}
	}
	cout << res;
    return 0;
}
*/

(16)P1025 [NOIP 2001 提高组] 数的划分

题目概括

给定整数 \( n \)(\( 6 < n \leq 200 \))和 \( k \)(\( 2 \leq k \leq 6 \)),求将 \( n \) 分成 \( k \) 个不为空且不考虑顺序的不同分法的数量。

DFS+剪枝(减少重复)

// DFS+剪枝(减少重复)
#include<bits/stdc++.h>
using namespace std;

//三个变量:num(剩余待分的整数),part(剩余分的份数,初始为k),start(当前要选择的数的最小值,初始为1) 注:每次选择一个数后,下一个数的选择从当前数开始,以避免重复分法
int dfs(int num,int part,int start){
  //首先特殊情况
  if(part==1) return  1;
  int sum = 0;
  // 当前选择的数不能超过num/part,是一种剪枝操作,比如(6,2,1)时i<=3,i会有1、2、3中情况,但i=4时,剩余1份分配为2,这与前面的i=2,剩4重复了
  for(int i =start; i<= num/part;i++){
   sum += dfs(num-i,part-1,i);
  }
  return sum;
}

int main(){
  int n,k;
  cin >> n >>k;
  cout <<dfs(n,k,1)<<endl;
  return 0;
}

动态规划(递推)

//动态规划(递推)
//dp[i][j] :将i分成j份的种类数
//因为6<n,k>=6,所以每份至少为1
#include<bits/stdc++.h>
using namespace std;
int dp[201][6];

int main(){
  int n,k;
  cin >> n>>k;
  //初始化
  for(int i=1;i<=n;i++){
     dp[i][1] = 1;
  }
  for(int i =2;i<=n;i++){
    for(int j = 2;j<=k;j++)
    //dp[i-1][j-1] 表示组合中含1(当前),dp[i-j][j] 表示不含1,理解为先为j都分配1,故等价于i-j再分j份(每份>1)  " if(i>=j)"一定要写
    if(i>=j) dp[i][j] = dp[i-1][j-1] + dp[i-j][j];
  }
  cout <<dp[n][k];
  return 0;
}

(17)P1192 台阶问题

有 N 级台阶,你一开始在底部,每次可以向上迈 1∼K 级台阶,问到达第 N 级台阶有多少种不同方式。

//斐波那契数列思想,这道题的基础版:https://zhaotong.fun/2022/06/15/dynamic-programing.html

// class Solution {
// public:
//     int climbStairs(int n) {
//         vector<int>dp(n+1);
//         dp[1]=1;
//         dp[0]=1;
//         for(int i =2;i<=n;i++){
//             dp[i] = dp[i-1]+dp[i-2];
//         }
//         return dp[n];
//     }
// };

#include<bits/stdc++.h>
using namespace std;
const int mod = 100003;
int n,k,dp[100001];
int main(){
  cin >> n>>k;
  dp[0] = dp[1]=1;
  for(int i =2;i<=n;i++){
    for(int j=1;j<=k;j++){
      if(i>=j){
        dp[i] = (dp[i]+dp[i-j])%mod;  //dp[i-k]从第 i−k级台阶迈k级到达第 i 级台阶的所有可能方式,累加形式(不除模的话可以写成dp[i]+=dp[i-j])
      }
    }
  }
  cout << dp[n]%mod;
  return 0;
}

(18)P1464 Function

实现递归函数 \(w(a,b,c)\),按给定条件(如 \(a,b,c \leq0\) 或 \(>20\) 等不同情况的返回规则)计算结果,需优化以应对重复调用问题,多组输入(以 \(-1,-1,-1\) 结束),输出格式为 \(w(a, b, c) = ans\)。

//记忆化减少重复
#include<bits/stdc++.h>
using namespace std;
//定义数组储存计算过的数,21是因为>20的返回w(20,20,20)
int w[21][21][21]; //已经计算过的 w(a, b, c) 的值
bool v[21][21][21];//标记 w(a, b, c) 是否已经被计算过,true则返回w[a][b][c]
//w[n][m][z],v[n][m][z]初始为 0和false
int f(long long a,long long b,long long c){  //f计算w(a,b,c)的值
  if(a<=0 or b<=0 or c<=0) return 1;
  if(a>20 or b>20 or c>20) return f(20,20,20);
  if(v[a][b][c]) return w[a][b][c];  //计算过则直接取值
  if(a<b & b<c){
    w[a][b][c] = f(a,b,c-1)+f(a,b-1,c-1)-f(a,b-1,c);
  }else{
    w[a][b][c] = f(a-1,b,c)+f(a-1,b-1,c)+f(a-1,b,c-1)-f(a-1,b-1,c-1);
  }
  v[a][b][c] = true; //标记
  return w[a][b][c];
}
int main(){

  long long a,b,c;
  // scanf("%lld %lld %lld",&a,&b,&c); //为了判断第一次a,b,c能否进入循环
  // while(a!=-1 or b!=-1 or c!=-1){
  //   printf("w(%lld, %lld, %lld) = %d\n",a,b,c,f(a,b,c));
  //   scanf("%lld %lld %lld",&a,&b,&c);
  // }  
  while(1){
    cin >>a>>b>>c;
    if(a==-1 & b==-1&c==-1) return 0;
    cout << "w("<<a<<", "<<b<<", "<<c<<")"<<" = "<<f(a,b,c) <<endl;
  }
}

(19)P5534 【XR-3】等差数列

给定等差数列的前两项 \(a_1\)、\(a_2\) 以及项数 n,求该等差数列的各项之和。

//直接上求和公式
#include<bits/stdc++.h>
using namespace std;
int main(){
  long  a,b,n,c;
  cin >> a>>b>>n;
  c= b-a;
  cout << a*n +n*(n-1)*c/2;
}

(20)P1036 [NOIP 2002 普及组] 选数

给定 \( n \) 个整数和整数 \( k \)(\( k < n \)),计算从这 \( n \) 个数中选 \( k \) 个相加,和为素数的组合种数。

//组合题+素数,全排列题目见P1706
#include<bits/stdc++.h>
using namespace std;
int m,k,res=0;
int nums[21];
//判素数
bool isprime(int n){
  if(n<2) return false;
  for(int i=2;i<=sqrt(n);i++){
    if(n%i==0) return false;
  }
  return true;
}

//DFS  s为搜索起始位置,cut为已选取的个数 sum为选取数的和
void dfs(int s,int cut,int sum){
  if(cut == k){
    if(isprime(sum)){
      res ++;
    }
  }
  for(int i=s;i<m;i++){
    dfs(i+1,cut+1,sum+nums[i]);
  }
}
//主函数
int main(){
  cin >> m>> k;
  for(int i=0;i<m;i++){
    cin >> nums[i];
  }
  dfs(0,0,0);
  cout << res<<endl;
  return 0;
}

(21)P1028 [NOIP 2001 普及组] 数的计算

给定正整数 \( n \),构造合法数列:单个 \( n \) 是合法数列;合法数列末尾可加不超过最后一项一半的正整数生成新合法数列。求合法数列的总个数。

//动态规划思想
#include<bits/stdc++.h>
using namespace std;
int nums[1001];
//核心:理解nums[i]的含义->以i为结尾的合法数列的总数(注意是总数)
int main(){
  int n;
  cin >> n;
  // 对于每个 i,尝试所有可能的 j(可以添加到 i 后面的数字)
  for(int i=1;i<=n;i++){
    //j从1开始(题目要求正整数)  
    for(int j=1;j<=i/2;j++){
      nums[i] += nums[j];   // 将 j 的合法数列数量累加到 i 的总数中
    }
    nums[i] ++; //  i自身单独成数列的情况
  }
  cout << nums[n];
  return 0;
}

五、模拟题

(22)P1003 [NOIP 2011 提高组] 铺地毯

已知 n 张矩形地毯按编号从小到大依次铺在平面直角坐标系第一象限,后铺的覆盖先铺的。给出每张地毯的左下角坐标及 x、y 方向长度,以及一个点的坐标,求覆盖该点的最上面那张地毯的编号,无覆盖则输出 \(-1\)。

#include<bits/stdc++.h>
using namespace std;
const int  maxn = 100005;
int a[maxn],b[maxn],c[maxn],d[maxn];
int main(){
  int n;
  cin >> n;
  for(int i=0;i<n;i++){
    cin >> a[i] >>b[i]>>c[i]>>d[i];
  }
  int x,y,res=-1;
  cin >>x>>y;
  for(int i =0;i<n;i++){
    if(x>=a[i]&&y>=b[i]&&x<=a[i]+c[i]&&y<=b[i]+d[i]){
      res  = i+1;   //层数加1
    }
  }
  cout << res;
  return 0;
}

(23)P1328 [NOIP 2014 提高组] 生活大爆炸版石头剪刀布

两人玩石头剪刀布升级版游戏(含剪刀、石头、布、蜥蜴人、斯波克五种手势,有特定胜负关系),出拳有周期性。给定共 N 次猜拳,以及小 A、小 B 出拳的周期长度和周期内出拳规律,统计 N 次猜拳后两人得分。


#include<bits/stdc++.h>
using namespace std;

int vs[5][5] = { // 胜负关系表(核心规则)
  {0,0,1,1,0}, // 剪刀:赢布、蜥蜴人
  {1,0,0,1,0}, // 石头:赢剪刀、蜥蜴人
  {0,1,0,0,1}, // 布:赢石头、斯波克
  {0,0,1,0,1}, // 蜥蜴人:赢布、斯波克
  {1,1,0,0,0}  // 斯波克:赢石头、剪刀
};

int n,na,nb;// 总猜拳次数、小A周期、小B周期
int x=0,y=0;// 记录得分
int main(){
  cin>> n>>na>>nb;
  int a[na],b[nb];
  for(int i=0;i<na;i++) cin >> a[i];
  for(int i=0;i<nb;i++) cin >>b[i];
  for(int i =0;i<n;i++){
    int pa = a[i%na];  // 小A出拳
    int pb = b[i%nb];  // 小B出拳
    x += vs[pa][pb];
    y += vs[pb][pa];
  }
  cout << x<<" "<<y;
  return 0;
}
//笨方法 纯模拟
// #include<bits/stdc++.h>
// using namespace std;
// int f(int a,int b ){
//   if (a == b) return 0;
// 	if (a == 0 && b == 1) return 1;
// 	if (a == 0 && b == 2) return 2;
// 	if (a == 0 && b == 3) return 2;
// 	if (a == 0 && b == 4) return 1;
// 	if (a == 1 && b == 0) return 2;
// 	if (a == 1 && b == 2) return 1;
// 	if (a == 1 && b == 3) return 2;
// 	if (a == 1 && b == 4) return 1;
// 	if (a == 2 && b == 0) return 1;
// 	if (a == 2 && b == 1) return 2;
// 	if (a == 2 && b == 3) return 1;
// 	if (a == 2 && b == 4) return 2;
// 	if (a == 3 && b == 0) return 1;
// 	if (a == 3 && b == 1) return 1;
// 	if (a == 3 && b == 2) return 2;
// 	if (a == 3 && b == 4) return 2;
// 	if (a == 4 && b == 0) return 2;
// 	if (a == 4 && b == 1) return 2;
// 	if (a == 4 && b == 2) return 1;
// 	if (a == 4 && b == 3) return 1;
// }
// //周期通过%来循环
// int main(){
//   int n,na,nb;
//   cin>>n>>na>>nb;
//   int a[201],b[201];
//   int x =0,y= 0;  //计数
//   for(int i=1;i<na;i++) cin>>a[i];
//   for(int i=1;i<na;i++) cin >>b[i];
//   for(int i=1;i<n;i++){
//     int pa = i%na,pb = i%nb;////pa,pb= na,b%T(周期)  循环
//     if(pa ==0 ) pa = na;
//     if(pb == 0) pb = nb;  //边界情况
//     int p = f(a[pa],b[pb]);
//     if(p!=0){
//       if(p==2) x++;
//       else if (p==1) y++;
//     }
//   }
//   cout << x<<" "<<y;
//   return 0;
// }

(24)P1067 [NOIP 2009 普及组] 多项式输出

给定一元 \( n \) 次多项式的次数 \( n \) 及各项系数,按次数递减、只含非零系数等规则,格式化输出该多项式。

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n, a; cin >> n;
	for(int i=n; i>=0; i--){    //指数递减给出多项式,故倒序循环
		cin >> a;
		if(a){
			if(i<n&&a>0) cout << '+';
			if(abs(a)>1||i==0) cout << a;
			if(a==-1&&i) cout << '-';     //a=-1特殊情况
			if(i>0) cout << 'x';
			if(i>1) cout << '^' << i;
		}
	}
	return 0;
}

(25)P1563 [NOIP 2016 提高组] 玩具谜题

n 个玩具小人围成圈,各有朝向(朝内 / 朝外)和职业。从第 1 个小人开始,按 m 条 “向左 / 右数 s 个小人” 的指令移动,求最终到达小人的职业。

结构体:当对象有多个相关联的属性,结构体可以方便地将这两个属性绑定在一起,便于统一管理和操作

//结构体:当对象有多个相关联的属性,结构体可以方便地将这两个属性绑定在一起,便于统一管理和操作
#include<bits/stdc++.h>
using namespace std;

//定义结构体
struct node
{
    bool f;  //表示玩具小人的朝向
    string name; //职业
};
node nums[100001];

int main(){ // 修正了拼写错误
    int n,m,a,s;
    cin >>n>>m;
    for(int i = 0;i<n;i++) cin>> nums[i].f >> nums[i].name;
    int st = 0;
    for(int i =0;i<m;i++){
        cin >> a>>s;
        if(nums[st].f^ a)//异或运算  (逆时针)
            st = (st+s)%n;// 计算新的位置,使用模运算确保在环内
        else 
            st = (st-s+n)%n;// 计算新的位置,加n后再取模避免负数
    }
    cout<< nums[st].name;
    return 0;
}

不使用结构体

//不使用结构体
#include <iostream>
#include <string>
using namespace std;

int main() {
    int n, m, a, s;
    cin >> n >> m;

    // 声明两个数组分别存储朝向和职业
    bool flag[100001];   // 存储每个小人的朝向
    string name[100001]; // 存储每个小人的职业

    for (int i = 0; i < n; i++) {
        cin >> flag[i] >> name[i];
    }

    int start = 0;
    for (int i = 0; i < m; i++) {
        cin >> a >> s;
        if (flag[start] ^ a) 
            start = (start + s) % n;
        else 
            start = (start - s + n) % n;
    }
    cout << name[start];

    return 0;
}

(26)P1179 [NOIP 2010 普及组] 数字统计

统计给定范围 \([L, R]\) 内所有整数中数字 2 出现的总次数。

#include<bits/stdc++.h>
using namespace std;
int main(){
  int l,r,res = 0;
  cin >> l>>r;
  for(int i = l;i<=r;i++){
    int tmp= i;
    while(tmp>1){
      if(tmp%10 ==2) res ++;
      tmp/=10;
    }
  }
  cout << res;
  return 0;
}

(27)P2615 [NOIP 2015 提高组] 神奇的幻方

给定奇数 \( N \),按规则构造 \( N×N \) 幻方:1 放第一行中间,后续数 \( K \) 按“优先放右上,右上有数则放下”等规则填充,使每行、每列及对角线数字和相同。

#include <iostream>
using namespace std;

int main() {
    int n;
    int k = 1, x, y; //k表示第几个数,x,y表示k所在的位置
    int a[45][45] = {};
    cin >> n;

    for (k = 1; k <= n * n; k++) { //使用for循环来控制k的值
        if (k == 1) { //特判,最开始时
            x = 1;
            y = n / 2 + 1; //一行的中间位置
        } else if (x == 1 && y != n) {
            x = n;
            y++;
        } else if (y == n && x != 1) {
            x--;
            y = 1;
        } else if (x == 1 && y == n) {
            x++;
        } else {
            if (a[x-1][y+1]) {
                x++;
            } else {
                x--;
                y++;
            }
        }
        a[x][y] = k; //填写k
    }

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            cout << a[i][j] << " ";
        }
        cout << "\n";
    }

    return 0;
}

----2025.9.3

写到这里主包也是有点感慨,今天主包拿到了整个保研过程中的第一个offer,此前也是屡屡碰壁,甚至有的211的夏令营都进不去(入营的不是宣讲就是纯实践营,前后准备了很久的某九的夏令营面试也喜提低位候补(T_T)),一度让我开始怀疑自己的能力。现在开始主包的刷题不在单纯针对保研,也是为了大四投递实习,一起加油吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值