目录
sort函数
- sort 是复合排序(快排为主)
- sort (起始地址,结束地址,排序方法cmp);
结束地址:最后一个元素地址+1
排序方法cmp:默认是从小到大 - ⭐自定义结构必须定义 cmp 或 类内重载<
#include<iostream>
#include <algorithm> //sort
using namespace std;
bool cmp(const int &a, const int &b) {
return a > b;
}
int main() {
int num[55] = {9, 2, -5, 5, 7, 22};
sort(num, num + 6); //num[0]~num[5]
sort(num + 1, num + 4); //num[1]~num[3]
sort(num, num + 6, greater<int>()); //从大到小
sort(num, num + 6, cmp); //自定义排序方法cmp
for (int i = 0; i < 6; i++) {
cout << num[i] << " ";
}
cout << endl;
return 0;
}
#include<iostream>
#include <string>//string
#include <algorithm>
using namespace std;
struct student {
int num, age, s;
string name; //代替char name[20],直接使用 = 赋值
bool operator< (const student &b) const {
return this->s > b.s;
}
};
/*
bool cmp(const student &a, const student &b) {
if (a.s == b.s) return a.num > b.num;
return a.s > b.s;
}
*/
int main() {
student a_202103[205];
a_202103[0].name = "wu";
a_202103[0].num = 1;
a_202103[0].age = 20;
a_202103[0].s = 60;
a_202103[1].name = "hu";
a_202103[1].num = 2;
a_202103[1].age = 25;
a_202103[1].s = 150;
a_202103[2].name = "xu";
a_202103[2].num = 3;
a_202103[2].age = 20;
a_202103[2].s = 60;
sort(a_202103, a_202103 + 3);
// sort(a_202103, a_202103 + 3, cmp);
for (int i = 0; i < 3; i++) {
cout << a_202103[i].num << " " << a_202103[i].name << " " << a_202103[i].s << endl;
}
return 0;
}
struct
c中struct是类型,c++中struct是类,struct的成员的默认访问权限为public,class为private。
381. 谁拿了最多奖学金
- c++ 引用的方便
#include <iostream>
#include <algorithm>
#include <string> // c++ string
using namespace std;
struct student {
string name;
int num, avg, cla, paper, m; // 对节点进行排序
char off, west;
};
bool cmp(const student &a, const student &b) {
if (a.m == b.m) return a.num < b.num;
return a.m > b.m;
}
student stu[105];
int n, ans;
int func(student &a) {
a.m = 0;
if (a.avg > 80 && a.paper >= 1) a.m += 8000;
if (a.avg > 85 && a.cla > 80) a.m += 4000;
if (a.avg > 90) a.m += 2000;
if (a.avg > 85 && a.west == 'Y') a.m += 1000;
if (a.cla > 80 && a.off == 'Y') a.m += 850;
return a.m;
}
int main() {
cin >> n;
for (int i = 0; i < n; i++) {
cin >> stu[i].name >> stu[i].avg >> stu[i].cla >> stu[i].off >> stu[i].west >> stu[i].paper;
stu[i].num = i;
ans += func(stu[i]); // c++引用
}
sort(stu, stu + n, cmp);
cout << stu[0].name << endl << stu[0].m << endl << ans << endl;
return 0;
}
#include<iostream>
#include <string>
#include <algorithm>
using namespace std;
struct person {
string name;
int num, avg, cla, paper, m;
char off, west; //official干部
};
int n, all;
person stu[105];
bool cmp(const person &a, const person &b) {
if (a.m == b.m) return a.num < b.num;
return a.m > b.m;
}
int func(int i) {
int t = 0;
if (stu[i].avg > 80 && stu[i].paper >= 1) t += 8000;
if (stu[i].avg > 85 && stu[i].cla > 80) t += 4000;
if (stu[i].avg >90) t += 2000;
if (stu[i].avg > 85 && stu[i].west == 'Y') t += 1000;
if (stu[i].cla > 80 && stu[i].off == 'Y') t += 850;
return t;
}
int main() {
cin >> n;
for (int i = 0; i < n; i++) {
cin >> stu[i].name >> stu[i].avg >> stu[i].cla >> stu[i].off >> stu[i].west >> stu[i].paper;
stu[i].num = i; //编号
stu[i].m = func(i); //奖学金
all += stu[i].m;
}
sort(stu, stu + n, cmp);
cout << stu[0].name << endl << stu[0].m << endl << all << endl;
return 0;
}
375. 奖学金
#include <iostream>
#include <algorithm>
using namespace std;
struct student {
int num, c, m, e, all;
};
bool cmp(const student &a, const student &b) {
if (a.all == b.all) {
if (a.c == b.c) return a.num < b.num;
return a.c > b.c;
}
return a.all > b.all;
}
int n;
student stu[305]; // 对象数组
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> stu[i].c >> stu[i].m >> stu[i].e;
stu[i].all = stu[i].c + stu[i].m + stu[i].e;
stu[i].num = i;
}
sort(stu + 1, stu + n + 1, cmp); // 从1开始存,为了序号num不用+1
for (int i = 1; i <= 5; i++) {
cout << stu[i].num << " " << stu[i].all << endl;
}
return 0;
}
380. 大统领投票
#include <iostream>
#include <string> //c++ string便于比较(长度/字典序)
#include <algorithm>
using namespace std;
struct node {
int num;
string s;
}; //注意分号
int n;
node p[105];
bool cmp(const node &a, const node &b) {
if (a.s.size() == b.s.size()) {
return a.s > b.s; //string可以直接比较字典序
}
return a.s.size() > b.s.size();
}
int main() {
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> p[i].s;
p[i].num = i; //序号
}
sort(p + 1, p + n + 1, cmp);
cout << p[1].num << endl << p[1].s <<endl;
return 0;
}
cstring与string
- cstring即string.h,是c语言库(.h去掉,前面加c) ,string是c++库
- c中字符串本质是字符数组 char s[105],cstring本质上都是对字符数组进行操作,例如strlen(s)会去遍历字符数组,找最后的’\0’
- c++中string是类,声明一个对象 string s; s是个对象,是个复合结构,里面有 char[ ]、size_t
- c++把c语言中不方便的地方进行了封装
c中s=“123” 错误
c++中s=“123” 正确,因为它重载了等号=,再如s=s+“456”
386. 吃瓜群众
#include <iostream>
#include <algorithm>
using namespace std;
struct node {
int num, ind;
};
node wm[100005];
int n, m;
bool cmp(const node &a, const node &b) {
return a.num < b.num;
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> wm[i].num;
wm[i].ind = i;
}
sort(wm + 1, wm + n + 1, cmp);
for (int i = 0; i < m; i++) {
int t, l = 1, r = n;
cin >> t;
while (l <= r) {
int mid = (l + r) / 2; // 越界风险,采用mid=l+(r-l)/2
if (t == wm[mid].num) {
cout << wm[mid].ind << endl;
break;
}
if (t > wm[mid].num) l = mid + 1;
else r = mid - 1;
}
if (l > r) cout << 0 << endl; // 出循环不是因为break
}
return 0;
}
387. 吃瓜群众升级版
- 最大的最小,最长的最短…类似就是二分问题的表述
- 找到二分的是什么?下标或答案
- 判断0000011111或1111100000的技巧:借助左右两边的小数进行判断
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
struct node {
int ind, num;
};
bool cmp(const node &a, const node &b) {
return a.num < b.num;
}
int n, m;
node wm[100005];
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
wm[i].ind = i;
scanf("%d", &wm[i].num);
}
wm[0].ind = 0, wm[0].num = 2000000000; // 法2:虚拟瓜堆(序号0,数量无限大)
sort(wm, wm + n + 1, cmp);
for (int i = 0; i < m; i++) {
int t, l = 0, r = n;
scanf("%d", &t); // 法1:与最大瓜堆进行判断
while (l != r) {
int mid = (l + r) / 2; // l+(r-l)/2不会越界,或把int变为long long
if (t > wm[mid].num) l = mid + 1;
else r = mid;
}
printf("%d\n", wm[l].ind);
}
return 0;
}
⭐二分的2种特殊情况
- 二分的本质:删掉不存在答案的区间
特殊情况1:000000011111
while(l != r){
int mid = (l + r) / 2;
if (n[mid] == 0) l = mid + 1;
else r = mid;//避免漏解
}
特殊情况2:1111110000000
while(l != r){
int mid = (l + r + 1) / 2;//+1避免死循环
if (n[mid] == 1) l = mid;//避免漏解
else r = mid - 1;
}
390. 原木切割
- 二分【小段木头的长度】,mid是临时的长度,之前二分的都是数组的下标(给数组,在数组中找值)
- 随着小段木头的长度的上升,段数下降,具有【单调性】
- 4.9能切出8段,5能切出8段,5.1只能切出6段,满足111000特殊情况
#include <iostream>
using namespace std;
int n, m, rmax, num[100005];
int func(int t) {
int ans = 0;
for (int i = 0; i < n; i++) ans += num[i] / t;
return ans;
}
int bs() {
// 111000 判断是哪种类型可以参考左右两边的小数
int l = 1, r = rmax;
while (l != r) {
int mid = (l + r + 1) >> 1;
int s = func(mid);
if (s >= m) l = mid;
else r = mid - 1;
}
return l; // 此时l==r
}
int main() {
cin >> n >> m;
for (int i = 0; i < n; i++) {
cin >> num[i];
rmax = max(rmax, num[i]);
}
cout << bs() << endl;
return 0;
}
389. 暴躁的程序猿
#include <iostream>
#include <algorithm>
using namespace std;
int n, m, num[100005];
// 最近距离为x时,能安排的人数
int func(int x) {
int t = 1, last = num[0]; // num[0]位置一定安排人
for (int i = 1; i < n; i++) {
if (num[i] - last >= x) {
t++;
last = num[i];
}
}
return t;
}
int bs() {
int l = 1, r = num[n - 1] - num[0];
while (l != r) {
int mid = (l + r + 1) >> 1; // mid临时答案
int s = func(mid); // 数组换函数
if (s >= m) l = mid;
else r = mid - 1;
}
return l;
}
int main() {
cin >> n >> m;
for (int i = 0; i < n; i++) {
cin >> num[i];
}
sort(num, num + n);
cout << bs() << endl;
return 0;
}
393. 切绳子(小数)
- 二分小数【循环条件】
- 直接舍掉2位后的小数,输出
#include <iostream>
#include <cstdio> // printf
#include <algorithm>
using namespace std;
int n, m;
double num[10005], tr; // tr右边界
int func(double x) {
int t = 0;
for (int i = 0; i < n; i++) {
t += num[i] / x;
}
return t;
}
double bs() {
double l = 0, r = tr;
while (r - l > 1e-5) { // 循环条件:大于预设精度
double mid = (l + r) / 2;
int s = func(mid);
if (s >= m) l = mid;
else r = mid;
}
return l;
}
int main() {
cin >> n >> m;
for (int i = 0; i < n; i++) {
cin >> num[i];
tr = max(tr, num[i]);
}
sort(num, num + n);
double t = bs();
printf("%.2lf\n", (int)(t * 100) / 100.0); // 强转取整直接舍掉小数
// printf("%.2lf\n", t - 0.005); // printf默认四舍五入
return 0;
}
82. 伐木
#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <algorithm>
using namespace std;
int n, m, num[1000005], tr;
long long func(int x) {
long long ans = 0;
for (int i = 0; i < n; i++) {
if (num[i] > x) ans += num[i] - x;
}
return ans;
}
int bs() {
int l = 0, r = tr;
while (l != r) {
int mid = ((long long)l + r + 1) / 2;
long long s = func(mid);
if (s >= m) l = mid;
else r = mid - 1;
}
return l;
}
int main() {
cin >> n >> m;
for (int i = 0; i < n; i++) {
cin >> num[i];
tr = max(tr, num[i]);
}
cout << bs() << endl;
return 0;
}
391. 数列分段
#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <algorithm>
using namespace std;
long long n, m, num[100005], l, r;
long long func(long long x) { // 最大和为x时,能将数组分为的段数
/*
long long now = 0, t = 0; // now当前和,t段数
for (int i = 0; i < n; i++) {
if (now + num[i] > x) {
t++;
now = num[i];
} else if (now + num[i] == x) {
t++;
now = 0;
} else {
now += num[i];
}
}
if (now != 0) t++; // 等于0是刚好最后一段
*/
long long now = 0, t = 0;
for (int i = 0; i < n; i++) {
if (now + num[i] <= x) { // 合并2种情况
now += num[i];
} else {
t++;
now = num[i];
}
}
t++; // 最后一段
return t;
}
int bs() {
while (l != r) {
long long mid = (l + r) / 2;
long long s = func(mid);
if (s <= m) r = mid;
else l = mid + 1;
}
return l;
}
int main() {
cin >> n >> m;
for (int i = 0; i < n; i++) {
cin >> num[i];
l = max(l, num[i]);
r += num[i];
}
cout << bs() << endl;
return 0;
}
394. 跳石头
#include <iostream>
using namespace std;
int l, r, n, m, num[50005];
// 最近距离为x时,需要移走的石头数量
int func(int x) { // func ==> 数组
int t = 0, last = 0; // last上一块石头位置
for (int i = 1; i <= n + 1; i++) {
if (num[i] - last < x) t++;
else last = num[i];
}
return t;
}
int bs() { // 二分
while (l != r) {
int mid = (l + r + 1) / 2;
int s = func(mid);
if (s <= m) l = mid;
else r = mid - 1;
}
return r;
}
int main() {
cin >> r >> n >> m;
for (int i = 1; i <= n; i++) { // 输入+处理边界
cin >> num[i];
l = min(l, num[i] - num[i - 1]);
}
num[n + 1] = r; // 起点num[0],终点num[n + 1]
l = min(l, num[n + 1] - num[n]);
cout << bs() << endl;
return 0;
}
⭐二分查找总结
- sort
- 二分的本质就是删掉不存在答案的区间,前提是单调
- 二分查找(二分小数)
- 二分的2种特殊情况
- 二分答案(对答案进行二分,答案必须满足单调性,用函数替代二分查找中的数组)
- 出现最大的最小、最小的最大类似描述,首先想到二分答案