文章目录
The First Week
读不在三更五鼓,功只怕一曝十寒。 ————郭沫若
。
一、前言
上周申请到了学生账号,这周开始使用Clion,还不是很熟练一着急就又开devc++了,尽量试试看用这个吧。
周一打了蓝桥杯模拟赛,div2,三所学校五十个人排第十四,十道题,ioi赛制,668分21.13h。
周三打了队内训练赛,由于后半程有事情,几乎都没再敲了,所以成绩很差,就不放上来了,直接补题吧,另外有人ak了,讲题的时候听了一下,发现很多技巧性的东西基本都没学会,还是得抓紧时间去学知识点。
周五第二十名,补题只做前七题了,可能效率会更高,把重心放在知识点上了,有些题目现阶段真的做不出来,完全没听过一些算法,赶紧补基础吧。
二、算法
1.二分算法
<1>(洛谷P8647)
1.22的蓝桥杯模拟赛F题,也就是蓝桥杯 2022 省赛 A 组 I 题。很多人都ac了,但有俩个数据一直过不去,ioi赛制最后拿了72分。晚上讲题后补题,一个很典型且简单的二分板子。补题后过了,之前也会二分但不太习惯用,一直都是暴力解法,以后得有意识用二分模板。
代码:
#include <iostream>
using namespace std;
int n,k,h[100005],w[100005];
bool check(int t){
int ans=0;
for(int i=0;i<n;i++){
ans+=(h[i]/t)*(w[i]/t);
}return ans>=k;
}
int main() {
cin>>n>>k;
for(int i=0;i<n;i++){
cin>>h[i]>>w[i];
}
int l=0,r=100005;
while (l < r) {
int mid = (l + r + 1) / 2;
if (check(mid)){
l=mid;
}
else r=mid-1;
}
cout<<l;
return 0;
}
2.递归/DFS(深度优先搜索)
<1>(洛谷P3623)
在b站看了些dfs的视频,去找了个典型题和普及题,因为不是很会用dfs,所以还是写了一段时间的,就给这道普及题写个题解吧,一开始还报错来着。
题解:
题意较为简单,从n名小朋友中抽取k人随机排序,按字典序输出。
开了俩个数组st和ans,定义了一个简单的dfs函数,由拍照学生的第一个人为第一层逐渐往下搜索。
代码:
#include <iostream>
using namespace std;
int st[20]; //表示选人的状态,1表示选过了,0表示没选过
int ans[20];//表示拍照的学生顺序,反复重新赋值
int n,k;
void dfs (int t){ //t表示当前遍历到哪个位置
if(t>k){ //选够人数即输出
for(int i=1;i<=k;i++){
cout<<ans[i]<<' ';
}cout<<endl;
return ;
}
for(int i=1;i<=n;i++){ //从第一个学生开始挑选,顺序遍历
if(!st[i]){ //st为0即没被选过,选上它!
st[i]=1;
ans[t]=i;
dfs(t+1); //选到下一层即第二个人
st[i]=0; //恢复初状态,有请下一个学生
}
}
}
int main () {
cin>>n>>k;
dfs(1);
return 0;
}
<2>(洛谷P1036)
还是不会做练习赛那道题,就又去学习了一下dfs,这道题试了一下剪枝,可以降低时间复杂度,等我研究一下dp就去做蓝桥杯模拟赛这道题
题解:
题意要求从n个数中抽取k个数字相加,所得和为素数的情况有几种。
此处代码dfs输入俩个形参,t表示当前遍历到的位置,start表示前面的数字都选过后,从这个数字start开始选用,可以类比于上一题的st数组。但相较于数组储存取用情况,采用start可以实现去重。可以额外关注一下bool类型IsPrime函数,判断素数的函数。另外这里熟悉了一下剪枝技巧,也就是把无法填满k个位置的情况直接舍去,可以见注释的式子。
代码:
#include <iostream>
using namespace std;
int n,k;
int pre[25]; //储存原数组
int ans=0; //记录素数个数
int arr[25]; //储存需要运算的数字
bool IsPrime(int sum){ //判断是否是素数
if(sum<2) return false;
for(int i=2;i*i<=sum ;i++){ //这有个时间复杂度较低算法,i*i<=sum,最好写成i<=sum/i的形式
if(sum%i==0)return false;
}
return true;
}
void dfs (int t,int start){
int sum=0;
if(t+n-start<k)return ; //浅浅试一下剪枝
if (t>k){ //完整式子(t-1+n-start+1)
for(int i=1; i <= k;i++){
sum=arr[i]+sum;
}
if (IsPrime(sum))ans++;
return ;
}
for(int i=start; i<=n; i++){ //简单的递归
arr[t]=pre[i];
dfs(t+1,i+1);
}
}
int main() {
cin>>n>>k;
for(int i=1; i<=n;i++){
cin>>pre[i];
}
dfs(1,1);
cout<<ans;
return 0;
}
此题的数据范围较小,可以暴力解法,上述解法用来熟悉dfs技巧
<3>(洛谷P8625)
1.22蓝桥杯模拟赛H题,同蓝桥杯 2015 省赛 B 组 J 题。晚上讲题后第二天补题时发现我还是不会,
梅开二度还是不会,但是这次看得懂题解了,可以试着自己写一下。dp好难啊,给dp开一个专门模板。
3.贪心算法
由局部最优解到全局最优解
<1>(洛谷P1223)
蛮水的一道题,struct加一个非常简单的贪心,主要用来熟悉贪心算法,贴个代码确认一下贪心算法是什么。
代码:
#include <iostream>
using namespace std;
int main() {
int n,x;
double p,q;
cin>>n>>x>>p>>q;
double r=p/q;
int a[n];
int time=x;
int t=1;
int night=x ;
for(int i=0;i<n;i++){
cin>>a[i];
time=time-a[i];
night-=a[i];
while(time<r*x*t){
t++;
time+=x;
}
if(night==0){
t++;
time+=x;
time+=a[i];
i--;
n--;
night=x;
}
}cout<<t;
}
<2>(洛谷P8508)
其实这题的题解根本不好写,练习赛的时候没有做出来,后来看了洛谷题解,发现可以用数学式子,注意开long long,由于我几乎是跟着别人的题解补题的,这题还是标记了一下贪心算法,有时间可以再做一遍。
4.map映射
<1>(洛谷B3691)
看了个视频发现这题原来还挺简单的,看到这题的时候真的是毫无思路,根本想不到这种方法。
题解:
题意给出俩个序列a,b,在序列中每遇到数字m包含在序列b中,则将其分为俩个段,数字m不包含在俩段内。求最终分成了几段。
一个map映射后,遍历a数组,找到的都标记st=0,确保连续数字被定义为一段,找不到的标记st=1使得定义下一段,ans用来储存最终有几个数组。
#include <iostream>
#include <map>
using namespace std;
map<int,int>t; //map映射,键值对
int main() {
int n,m;
int b;
cin>>n>>m;
int a[n];
for(int i=0;i<n;i++){
cin>>a[i];
}
for(int i=0;i<m;i++){
cin>>b;
t[b]=1; //初值值为0,用以区分b序列
}
int ans=0,st=1;
for(int i=0;i<n;i++){
if(t[a[i]]==0){ //a[i]未出现在b序列,初始状态
if(st==1)ans++;
st=0; //多数字段需要改变状态
}
else st=1;
}
cout<<ans<<endl;
return 0;
}
<2>(洛谷P8889)
其实没用map但因为是上一题的hard模式就放在这了,挺无语的在看题解的时候,发现二分竟然就能过,试了一下因为忘记开ll,全错了,过了会才反应过来。
代码:
#include <iostream>
#include <algorithm>
using namespace std;
long long int n,m,a[500005],b[500005],ans;
bool check(long long t){ //一个二分数组,判断t是否在数组b中
long long l=0,r=m-1,mid;
while(l<=r){
mid=(l+r+1)/2;
if(t<b[mid])r=mid-1;
else if(t>b[mid])l=mid+1;
else return true;
}
return false;
}
int main() {
cin>>n>>m;
for(int i=0;i<n;i++)scanf("%lld",&a[i]);
for(int i=0;i<m;i++)scanf("%lld",&b[i]);
sort(b,b+m);
for(int i=0;i<n-1;i++){
if(!check(a[i])&&check(a[i+1])){ans++;
} //多数字连续为一段
}
if(!check(a[n-1]))ans++; //最后一个为数字是特殊情况
printf("%lld",ans); //scanf和printf输入输出较快
return 0;
}
5.DP
<1>(洛谷P1048)
一个典型的01背包,知道但写不出来,当时急得要死,之前一直想学就拖着没学,看得懂写不出。当时用了个dfs?试了一下没过反正。
定义一个二维数组dp[i][j],可代表容量j的背包里i个物品取得的最大价值,可以用一个类递推,dp[i][j]=max(dp[i−1][j−w[i]])+v[i],dp[i−1][j]),前者为取的情况,w[i]是它的容量,v[i]是价值;后者为不取第i个物品,容量j不变。二者取大赋值给dp[i][j]。另初始化dp[1][0]=0,dp[0][1]=0,(可以不写会自动赋值为0)。
典型的01背包二维dp,过俩天可以再熟悉一遍。这道dp题用dfs写不出来,暂时没有看到别的方法。
6.STL
一些很模糊的数据结构或用法,浅浅记录一下吧
- unordered_set 可自动给无序数组去重
- std::set 自动给数组排序去重
- getline(std::cin,a), a为一串带空格的字符串
- std::ceil(x) 向上取整
- std::setprecision(2)==printf(“%0.2f”, )
三、总结
第一周都只是一些简单的算法入门,由于花了点时间看视频和做例题,很多题目都还没补,补了的部分没来得及写,还是想以专题为单位学下去,下周会补上别的题,当然这几个板块后面做到题,估计也会继续补充题目。他们说的前缀和,拓扑等等到底是什么啊…慢慢学吧。听了一血的讲题都是我没听过的知识点。