保研秋招实习机试准备——C++算法题(三)

上一篇:

计算机保研机试准备——C++算法题(二)-CSDN博客文章浏览阅读308次。进阶:C++算法第二篇 https://blog.csdn.net/weixin_47520540/article/details/151123489?spm=1001.2014.3001.5501

六、排序相关问题

排序这一块,对应机试题目需要使用的排序的建议直接使用sort函数(可自定义排序方式),但对于经典的排序算法原理还是要学一下的,企业面试时

这里推荐一个排序算法题文章和模板代码文章

必刷算法题之排序篇(题目及代码)---C++_c++冒泡排序题目-CSDN博客

C++模板实现十大排序算法_c++十大排序算法模板代码-CSDN博客

此外对于sort的自定义比较函数,C++官方技术文档是这样解释的:二元函数,接受范围内的两个元素作为参数,并返回一个可转换为bool的值。返回值指示作为第一个参数传递的元素是否按照其定义的特定严格弱排序优先于第二个参数。(我觉得没有什么不这个更清晰的解释了)

(28)P1059 [NOIP 2006 普及组] 明明的随机数

对 \( N \) 个 1 到 1000 间的随机整数,去除重复数后从小到大排序,输出不同数的个数及排序结果。

去重也可以使用stl中的unique

#include<bits/stdc++.h>
using namespace std;
int main(){
  int n,tmp;
  cin >> n;
  set<int> s;
  for(int i = 0;i<n;i++){
    cin >> tmp;
    s.insert(tmp);
  }
  cout << s.size()<<endl; 
  for(auto&i:s){
    cout<< i<<" ";
  }
  cout << endl;

  return 0;
}

//去重也可以使用stl中的unique
/*
set集合是c++ stl库中自带的一个容器,set具有以下两个特点:

1、set中的元素都是排好序的

2、set集合中没有重复的元素

常用操作:

begin()    返回set容器的第一个元素的地址

end()      返回set容器的最后一个元素地址

clear()    删除set容器中的所有的元素

empty()     判断set容器是否为空

max_size()   返回set容器可能包含的元素最大个数

size()      返回当前set容器中的元素个数

erase(it) 删除迭代器指针it处元素

insert(a) 插入某个元素*/

(29)P1068 [NOIP 2009 普及组] 分数线划定

根据报名选手的笔试成绩,按计划录取人数的150%(向下取整)划定面试分数线,输出分数线、进入面试的人数,以及这些选手的报名号和成绩(成绩高到低,同分时报名号小的在前)。

//结构体或者下标数组
#include<bits/stdc++.h>
using namespace std;
struct scoreline{
  int k,s;
}nums[5001]; //a存储结构体

//比较函数:
/*C++技术文档解释:二元函数,接受范围内的两个元素作为参数,并返回一个可转换为bool的值。返回值指示作为第一个参数传递的元素是否按照其定义的特定严格弱排序优先于第二个参数。*/
bool cmp(scoreline a,scoreline b){
  if(a.s!=b.s) return a.s>b.s;
  return a.k < b.k;
}

//主函数
int main(){
  int n,m;
  cin >> n>>m;
  for(int i=0 ;i<n;i++){
    cin >> nums[i].k >> nums[i].s;
  }
  sort(nums,nums+n,cmp);
  int t = m*1.5;  //面试线人数 int 自动向下转型
  //考虑与分数线重分的
  while(t<n){
    if(nums[t].s == nums[t-1].s) t++;
    else break;
  }
  cout <<nums[t-1].s<<' '<<t<<endl;
  for(int i =0;i<t;i++){
    cout << nums[i].k <<' '<< nums[i].s <<endl;

  }
  return 0;
}

(30)P1051 [NOIP 2005 提高组] 谁拿了最多奖学金

给定若干学生的期末成绩、班级评议成绩等信息,依据五种奖学金的不同条件,计算出获奖金最多的学生姓名、该学生奖金总数,以及所有学生的奖金总数。

#include<bits/stdc++.h>
using namespace std;
struct stu{
  string name;
  int s,sc,p,m;        //s期末平均成绩 sc班级评议成绩 p论文数 m奖金
  char x,y ;      // x是否是学生干部  y是否是西部省份学生
}nums[101];

//比较函数
bool cmp(stu a ,stu b){
  return a.m > b.m;
}

int main(){
  int n,sum=0;
  cin >> n;
  for(int i = 0;i<n;i++){
    int tmp_m=0; //奖金临时储存
    cin >> nums[i].name>>nums[i].s>>nums[i].sc>>nums[i].x>>nums[i].y>>nums[i].p;
    if(nums[i].s > 80 && nums[i].p >= 1) tmp_m += 8000;
    if(nums[i].s>85&& nums[i].sc>80) tmp_m+= 4000;
    if (nums[i].s > 90) tmp_m += 2000;
		if (nums[i].s > 85 && nums[i].y == 'Y') tmp_m += 1000;
		if (nums[i].sc > 80 && nums[i].x == 'Y') tmp_m += 850;
    nums[i].m = tmp_m;//奖奖可兼得
    sum += tmp_m;
  }
 // sort(nums,nums+n,cmp); 不全对 使用stable_sort 保持相等值相对顺序不变
 stable_sort(nums,nums+n,cmp);
  cout <<nums[0].name <<"\n"<<nums[0].m<<"\n"<<sum;
  return 0;
}

//普通模拟
/*#include<bits/stdc++.h>
using namespace std;
int n, a, b, e, sum, Sum, mx;
string s, ans;
int main () {
	cin >> n;
	for (char c, d; n--; Sum+=sum) {
		cin >> s >> a >> b >> c >> d >> e;
		sum=(a>80&&e)*8000+
			(a>85&&b>80)*4000+
			(a>90)*2000+
			(a>85&&d=='Y')*1000+
			(b>80&&c=='Y')*850;
		if (sum>mx)
			mx=sum,
			ans=s;
	} 
	cout << ans << '\n' << mx << '\n' << Sum;
	return 0;
}
*/

(31)P1908 逆序对

给定一段正整数序列,统计其中逆序对(满足 \(a_i > a_j\) 且 \(i < j\) 的有序对)的数量。

思路:在归并排序合并两个有序子数组时,当取右子数组元素时,左子数组中剩余的元素都能与该右子数组元素构成逆序对,从而累加得到逆序对总数。

//归并排序的核心思想是 “分治”:先将大区间拆分为小区间,排序后再合并。
#include<bits/stdc++.h>
using namespace std;
int n;
vector<int>a,tmp;//a存储原始序列 
//tmp归并排序的临时数组(用于合并阶段避免原数组操作覆盖)
long long res=0;
void merge(int l,int r){
  //对区间[l, r]内的元素进行归并排序,并统计逆序对数量。
  if(l==r)return;
  int m = l+(r-l)/2,i=l,j=m+1,k=l;
  //i左区间的起始指针,k临时数组c的起始指针
  merge(l,m);//递归排序左半区间
  merge(m+1,r);
  while(i<=m&&j<=r){//当左右区间都还有元素时,比较并合并
    if(a[i]<=a[j]) tmp[k++] =a[i++];
    else{
      tmp[k++] = a[j++];
      res += m-i+1;//由于递归,左右区间已排序,故i后的所有元素都与ap[j]构成逆序对
    }
  }
  //有一方区间为空,直接将剩余的添在合并的后面
  while(i<=m) tmp[k++] = a[i++];
  while(j<=r) tmp[k++] = a[j++];
  for(int i=l;i<=r;i++) a[i] = tmp[i];//复制回原数组
}
int main(){
  cin >> n;
  //不调整a,tmp会导致越界访问,vector动态大小是通过push_back等方法自动扩大容量
  //而未预先分配了任意位置的访问权限,刚定义的 vector<int>a,其size()是0
  a.resize(n+1);
  tmp.resize(n+1);
  for(int i=1;i<=n;i++) cin >> a[i];
  merge(1,n);
  cout << res;
  return 0;
}

七、二分问题

(32)P1024 [NOIP 2001 提高组] 一元三次方程求解

给定一元三次方程 \( ax^3 + bx^2 + cx + d = 0 \) 的系数 \( a,b,c,d \),已知方程有三个不同实根(范围在 \(-100\) 到 \(100\) 之间,且根间距≥1),从小到大输出这三个实根,精确到小数点后2位。

小数位数 使用scanf 和printf

暴力解法,可ak
 

#include<iostream>
#include<cstdio>
using namespace std;
double a,b,c,d;// 题目要的数据是小数点后2位所以定义首先用double
int num;// num用来记录解的个数 因为一元三次方程只有三个解  解达到三个以后就break掉 减少多余循环
int main()
{
	scanf("%lf%lf%lf%lf",&a,&b,&c,&d);// double类型用 lf 输入
	for(double i=-100.00;i<=100.00;i+=0.001)// 最后结果保存两位数 所以这里i每次加0.001(n只有100所以暴不了)
	{
		double j=i+0.001;
    double y1 = a*i*i*i+b*i*i+c*i+d;
    double y2 = a*j*j*j+b*j*j+c*j+d;
		if(y1>=0&&y2<=0||y1<=0&&y2>=0)// 若存在两个数x1,x2且x1<x2,f(x1)*f(x2)<0 则方程解肯定在x1~x2范围内   基本数学原理
		printf("%.2f ",i),num++;// 小数点后两位输出
		if(num==3) break;// 解达到三个break掉
	}
	return 0;
}
//小数位数 使用scanf 和printf

二分解法(注意浮点数二分的细微差异)

//二分解法
#include<bits/stdc++.h>
using namespace std;
double a,b,c,d;
int num=0;
double fc(double x){
  return a*x*x*x+b*x*x+c*x+d;//三次方程
}
int main(){
  double l,r,mid,x1,x2;
  scanf("%lf%lf%lf%lf",&a,&b,&c,&d);// double类型用 lf 输入
  for(int i=-100;i<100;i++){//两个根的差的绝对值>=1,每个大小为1的区间至多1个解
    //所以枚举步长为1,在每个长度1的区间内进行二分查找
    l=i,r = i+1;
    x1 = fc(l),x2 = fc(r);
    if(!x1){//左端点是0直接输出
      printf("%.2lf",l);
      num++;
    }
    if(x1*x2<0){
    //用 l=mid+1 或 r=mid-1 这类整数二分的更新方式并不适用于浮点数二分
      while(r-l>=0.001){
        mid = (r+l)/2;//由于while(r-l>=0.001),所以l,r都用mid更新不会死循环
        if(fc(mid)*fc(r)<0) l=mid;// 根在[mid, r]
        else r = mid;// 根在[l, mid]
      }
      printf("%.2lf",r);
      num ++;
    }
    if(num==3) break;
  }
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值