作者:指针不指南吗
专栏:蓝桥杯倒计时冲刺🐾最后一周,复习学过的知识,刷题冲刺🐾
文章目录
- 1.高精度除法
- 2.扫地机器人
- 3.数的范围
- 4.A-B 数对
1.高精度除法
-
题目
链接: 794. 高精度除法 - AcWing题库
给定两个非负整数(不含前导 0) A,B,请你计算 A/B 的商和余数。
输入格式
共两行,第一行包含整数 A,第二行包含整数 B。
输出格式
共两行,第一行输出所求的商,第二行输出所求余数。
数据范围
1≤A的长度≤100000,
1≤B≤10000,
B 一定不为 0输入样例:
7 2
输出样例:
3 1
-
n次之后才 AC
#include<bits/stdc++.h> using namespace std; vector<int> div(vector<int> &A,int &b,int &r) //这里r一定要使用引用 { vector<int> C; r=0; for(int i=A.size()-1;i>=0;i--) //从最高开始除 { r=r*10+A[i]; C.push_back(r/b); //C 里面存的是 商,不是模,之前不同!! r%=b; //取模!与其他的不同 } reverse(C.begin(),C.end()); //反转,与其他运算保持一致 while(C.size()>1&&C.back()==0) C.pop_back(); //去掉前导0,除了加不用,其他都是需要的 return C; } int main() { string a; int b; cin>>a>>b; vector<int> A; for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0'); //-‘0’ 记得 int r=0; auto C=div(A,b,r); for(int i=C.size()-1;i>=0;i--) printf("%d",C[i]); printf("\n"); printf("%d",r); //最后输出 余数 return 0; }
-
反思
细节决定成败,我这细节全无 TvT
做的时候,不能只背模板,再想一下原因,为什么这么写,意义是什么!
2.扫地机器人
-
题目
链接: 扫地机器人 - 蓝桥云课 (lanqiao.cn)
小明公司的办公区有一条长长的走廊,由 N 个方格区域组成,如下图所示。
走廊内部署了 K 台扫地机器人,其中第 i 台在第 A i 个方格区域中。已知扫地机器人每分钟可以移动到左右相邻的方格中,并将该区域清扫干净。
请你编写一个程序,计算每台机器人的清扫路线,使得
- 它们最终都返回出发方格,
- 每个方格区域都至少被清扫一遍,
- 从机器人开始行动到最后一台机器人归位花费的时间最少。
注意多台机器人可以同时清扫同一方块区域,它们不会互相影响。
输出最少花费的时间。 在上图所示的例子中,最少花费时间是 6。第一台路线:2-1-2-3-4-3-2,清 扫了 1、2、3、4 号区域。第二台路线 5-6-7-6-5,清扫了 5、6、7。第三台路线 10-9-8-9-10,清扫了 8、9 和 10。
输入描述
第一行包含两个整数 N,K。
接下来 K 行,每行一个整数 A i。
其中,1≤K<N≤ 1 0 5 10^5 105 ,1≤A i≤N。
输出描述
输出一个整数表示答案。
输入输出样例
示例
输入
10 3 5 2 10
输出
6
-
第一次
我的思路是算出每一个机器人的最短时间取最大值,使用 BFS 不断向两边扩展,直到归位就跳出BFS。但是,没有办法保证每个地方都扫了
-
题解
/* 解题思路: 本题为一道比较明显的二分题目。 题目要求最少花费时间。由于每个机器人的工作时间可能不同,那么这些机器人各自的花费时间中的最大值(设为 t )的就是本题要求的答案, 需要做的是使得 t 最小。将最大花费时间(t)最小化,显然需要使用二分求解。 假设某个机器人需要清扫 a,b,c,d 四个格子,因为这个机器人清扫完还需要回到最初始的位置,所以无论这个机器人初始位置在什么地方, 其清扫路径的本质都是重复两次 a 到 b,b 到 c,c 到 d 的过程,花费时间为 6,由此,假设某个机器人清扫的格子范围为 l, 那么这个机器人花费的时间为 (l-1)\times*2。所以只需要对机器人清扫的范围(l)进行二分即可,最后的答案为 t=(l-1)\times*2。 显然当每个机器人清扫的范围大小相同时,花费时间最小。 可以对清扫范围进行二分,然后验证其答案的正确性即可,判断条件是清扫范围可以使得每个格子都能够扫到 可以明显的知道,最左边的格子由左边第一台机器人清扫,花费时间是最少的,在此可以采用贪心的思想, 让每台机器人都要优先清扫其左边还未扫的到格子,然后再往右扫,在二分得到的范围下往右扫得越多越好, 这样可以减少右边下一个机器人需要往左扫的范围,增加其往右扫的范围,以此类推,可减少清扫时间。 综上,本题采用二分加贪心的思想解答。 */ #include<bits/stdc++.h> using namespace std; int robot[1000005];//机器人位置 int n, k; bool check(int len) { int sweep = 0;//sweep代表清扫到了哪个位置 for(int i = 1; i <= k; i++) { if(robot[i] - len <= sweep)//如果当前机器人只扫左侧,能够覆盖左侧未清扫的位置,则可进行当前机器人的清扫 { if(robot[i] <= sweep)//如果当前机器人已经处于清扫过的位置,则当前机器人只扫右侧区域 sweep = robot[i] + len - 1; else//否则从上一个清扫到的位置继续 sweep += len; } else//当前机器人只扫左侧,不能覆盖左侧未清扫的位置,当前方案不可行,返回 return 0; //cout<<sweep<<endl; } return sweep>=n; //表示当前方案可行 } int main() { cin >> n >> k; for(int i = 1; i <= k; i++) { cin >> robot[i]; } sort(robot + 1, robot + k + 1);//首先对机器人的位置进行排序 int L=0, R=n, M, ans; while(L <= R)//二分清扫范围 { M = (L + R) / 2; if(check(M))//如果当前方案可行,则缩小清扫范围,试图寻找更小的方案 { R = M - 1; ans = M; } else//如果方案不可行,则扩大清扫范围,寻找可行方案 L = M + 1; } cout << (ans - 1) * 2 << endl;//计算并输出答案 return 0; }
-
二分模板 复习一下
- 找某个值在区间里面是否存在
int BinSearch(int a[],int low,int high,int k) { if(low<=high){ //当前区间存在元素 int mid=(low+high)/2; if(a[mid]==k) return mid; //找到后返回其下标 if(a[mid]<k) return BinSearch(int a[],int low,int mid-1,int k); if(a[mid]>k) return BinSearch(int a[],int mid+1,int high,int k); }else{ return -1; //区间不存在元素,返回 -1 } }
- 返回左边 起始值
int l=0,r=n; while(l<r) { int mid=l+r>>1; if(check(mid)>=k) r=mid; //起始节点 else l=mid+1; }
- 返回右边 后继值
int l=0,r=n-1; while(l<r) { int mid=l+r+1>>1; if(a[mid]<=k) l=mid; //终止节点 else r=mid-1; }
-
反思
什么问题可以运用二分搜索算法技巧?
-
首先,你要从题目中抽象出一个自变量
x
,一个关于x
的函数f(x)
,以及一个目标值target
。 -
同时,
x
,f(x)
,target
还要满足以下条件:f(x)
必须是在x
上的单调函数(单调增单调减都可以)题目是让你计算满足约束条件
f(x) == target
时的x
的值。
最重要的就是抽象,这得需要多刷题,才能抽象出来…努力ing
-
3.数的范围
-
题目
链接: 789. 数的范围 - AcWing题库
给定一个按照升序排列的长度为 n 的整数数组,以及 q 个查询。
对于每个查询,返回一个元素 k 的起始位置和终止位置(位置从 0 开始计数)。
如果数组中不存在该元素,则返回
-1 -1
。输入格式
第一行包含整数 n 和 q,表示数组长度和询问个数。
第二行包含 n 个整数(均在 1∼10000 范围内),表示完整数组。
接下来 q 行,每行包含一个整数 k,表示一个询问元素。
输出格式
共 q 行,每行包含两个整数,表示所求元素的起始位置和终止位置。
如果数组中不存在该元素,则返回
-1 -1
。数据范围
1≤n≤100000
1≤q≤10000
1≤k≤10000输入样例:
6 3 1 2 2 3 3 4 3 4 5
输出样例:
3 4 5 5 -1 -1
-
第一次 AC 100%
#include<bits/stdc++.h> using namespace std; const int N=1e5+10; int n,m; int a[N]; int main() { scanf("%d%d",&n,&m); for(int i=0;i<n;i++) cin>>a[i]; while(m--) { int k; scanf("%d",&k); //二分查找 int l=0,r=n; while(l<r) { int mid=l+r>>1; if(a[mid]>=k) r=mid; else l=mid+1; } if(a[l]==k) cout<<l<<' '; else{ puts("-1 -1"); continue; } l=0,r=n; while(l<r) { int mid=l+r+1>>1; if(a[mid]<=k) l=mid; else r=mid-1; } if(a[l]==k) cout<<l<<endl; else cout<<l-1<<endl; //已经确定数组中存在这个数,如果不是要找的值,说明我们找到是大于k的值,需要退一步 return 0; }
4.A-B 数对
-
题目
链接: P1102 A-B 数对 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
给出一串正整数数列以及一个正整数 C C C,要求计算出所有满足 A − B = C A - B = C A−B=C 的数对的个数(不同位置的数字一样的数对算不同的数对)。
输入格式
输入共两行。
第一行,两个正整数 N , C N,C N,C。
第二行, N N N 个正整数,作为要求处理的那串数。
输出格式
一行,表示该串正整数中包含的满足 A − B = C A - B = C A−B=C 的数对的个数。
样例 1
样例输入 1
4 1 1 1 2 3
样例输出 1
3
提示
对于 75 % 75\% 75% 的数据, 1 ≤ N ≤ 2000 1 \leq N \leq 2000 1≤N≤2000。
对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 2 × 1 0 5 1 \leq N \leq 2 \times 10^5 1≤N≤2×105, 0 ≤ a i < 2 30 0 \leq a_i <2^{30} 0≤ai<230, 1 ≤ C < 2 30 1 \leq C < 2^{30} 1≤C<230。
-
第一次 AC 75%
#include<bits/stdc++.h> using namespace std; const int N=2010; int a[N]; int main() { int n,k; cin>>n>>k; for(int i=0;i<n;i++) cin>>a[i]; sort(a,a+n); int cnt=0; for(int i=0;i<n;i++) { for(int j=i+1;j<n;j++) { if(a[j]-a[i]==k) cnt++; } } cout<<cnt; return 0; }
暴力解题,好爽a
-
第二次 模拟的题解 AC100%
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=2*1e5+10; map<ll,ll> m; ll a[N]; int main() { ll n,k; cin>>n>>k; for(ll i=0;i<n;i++) { cin>>a[i]; m[a[i]]++; //记录每个数在数组中出现的次数 } ll cnt=0; for(ll i=0;i<n;i++) { ll t=a[i]-k; //t=A-C if(t<=0) continue; cnt+=m[t]; //直接加上t出现的次数,map真不错 } cout<<cnt; return 0; } //十年OI一场空,不开long long见祖宗
绷不住了,N范围开小了,找了半小时bug
-
反思
- 转换思路的重要性,题解中把 A-B=C 转换成 A-C=B,查找B的个数
- 利用 map 键值对来存 每一数在数组中出现的次数,然后就可以直接使用了
- 开 long long 不要犹豫