蒟蒻的2023蓝桥
看题居然把数据范围的优先级看的很低,很多问题可能很难解决,但是数据小,优雅一点的暴力就能做。
另外数据范围也能提示你用什么算法。
A.日期统计
没有意识到后面的2023统计的日期会与前面的重复。
另外dfs写的时候脑袋晕晕的框架都有点想不清,实际上就八个位置,100种可能。
所以dfs以深度来看。
B.01串的熵
直接跳过了。。
实际上直接枚举就行,另外注意精度1e-4,以及知道log2函数
C.冶炼金属
每条记录计算最大值与最小值,最后为了满足所有记录,取每个最大值中的最小值,取每个最小值中的最大值。
另一种做法,二分。
求满足所有记录的最大值与最小值。
二分求取满足要求的值时,可以转化为求大于等于要求的最小值(或相反),同时二分的check值与mid的对应关系还是要看的hh。
当答案很好验证时,都可以试试二分。
D.飞机降落
数据范围很小,全排列加一点点贪心的决策。
E.接龙数列
我们无法得知每个数是否需要删除,那么则需要讨论来判断,dp的感觉。
线性dp
f[i][j]表示考虑前i个数结尾为j的最小删除次数,尽管是简单的dp,我写起来还是很吃力,初始化0维为0表示兼容,记住了。
F.岛屿个数
被完全的框住意味着无论是直线还是斜角都没法走过来。
考试时只会走直线,导致错误。
题解的优雅解法,在外围套一圈0,从一个点开始向8个方向搜0就行,另外不用像我那样,将接触的1全部标记起来。
搜完后留下的空洞即岛屿。
在考试中还是聪明了一点,没有像之前那样搜索加并查集,直接循环如果没走过就搜就行了,st数组标记一下走过的地方,经典的搜索连通块方式。。。
#include<bits/stdc++.h> using namespace std; #define endl '\n' typedef long long LL; typedef pair<int,int> PII; const int INF=0x3f3f3f3f; const int N=55; char g[N][N]; bool st[N][N]; int n,m; //0,0没关系,已经走过了,可以枚举9宫格 优雅 int dx9[]={-1,0,1},dy9[]={-1,0,1}; int dx[]={-1,0,1,0},dy[]={0,1,0,-1}; void dfs0(int x,int y){ st[x][y]=true; for(int i=0;i<3;i++){ for(int j=0;j<3;j++){ int a=x+dx9[i],b=y+dy9[j]; if(a>=0&&a<=n+1&&b>=0&&b<=m+1&&g[a][b]=='0'&&!st[a][b]){ dfs0(a,b); } } } } void dfs2(int x,int y){//把空洞填满 st[x][y]=true; for(int i=0;i<4;i++){ int a=x+dx[i],b=y+dy[i]; if(a>0&&a<=n&&b>0&&b<=m&&!st[a][b]){ dfs2(a,b); } } } void solve(){ cin>>n>>m; memset(g,0,sizeof g); memset(st,0,sizeof st); for(int i=1;i<=n;i++)cin>>g[i]+1;//外圈套0 for(int i=0;i<=n+1;i++)g[i][m+1]=g[i][0]='0'; for(int i=0;i<=m+1;i++)g[n+1][i]=g[0][i]='0'; dfs0(0,0); int res=0; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(!st[i][j]){ dfs2(i,j); res++; } } } cout<<res<<endl; } int main(){ ios::sync_with_stdio(false); cin.tie(0); int T; cin>>T; while(T--){ solve(); } return 0; }
G.子串简写
本质是快速判断距离c1大于等于k-1的c2有多少个。
做法一:记录c2的位置,枚举c1,二分查找距离大于等于k-1的下标,末下标减去下标得到个数
做法二:后缀和,毕竟只需要查看某个区间有多少个c2罢了
#include<bits/stdc++.h> using namespace std; #define endl '\n' typedef long long LL; typedef pair<int,int> PII; const int INF=0x3f3f3f3f; const int N=5e5+10; int cnt[N]; int main(){ ios::sync_with_stdio(false); cin.tie(0); int k; cin>>k; string s; char a,b; cin>>s>>a>>b; for(int i=s.size()-1;~i;i--){ cnt[i]=(s[i]==b); cnt[i]+=cnt[i+1]; } LL res=0; for(int i=0;i<s.size();i++){ if(s[i]==a&&i+k-1<s.size())res+=cnt[i+k-1]; } cout<<res<<endl; return 0; }
H.整数删除
双链表+优先队列/set+模拟
记住优先队列与set等价(用法上),priority_queue多提供了一个弹出队头,访问队头,set也能实现只是麻烦一点,但是set支持任意删除(根据value或地址)。
补:优先队列能够允许重复元素存在,set不行
所以如果不需要队头,那么可以直接使用set。
set的逆序初始化:set<int,greater<int>> 访问队头*S.begin 删除队头S.erase(S.begin())。
收获:priority_queue 也能O1进行任意删除,只要再加另一个优先队列,将要删除的元素存入其中,当2个优先队列的top()相等时,2个都pop()。
set与priority_queue的修改操作,二者都不能直接修改元素,只能通过间接的方法来修改,即删除再添加。
priority_queue的写法(set简单修改即可)
#include<bits/stdc++.h> using namespace std; #define endl '\n' typedef long long LL; typedef pair<int,int> PII; typedef pair<LL,int> PLI; const int INF=0x3f3f3f3f; const int N=5e5+10; int l[N],r[N],idx; LL e[N]; int n,k; priority_queue<PLI,vector<PLI>,greater<PLI>>q,re;//value and id void init(){ l[1]=0,r[0]=1; idx=2; } void insert(int k,int x){ e[idx]=x; l[idx]=k,r[idx]=r[k]; l[r[k]]=idx,r[k]=idx++; } void remove(int k){ //需要特判边界 if(l[k]!=0){ re.emplace(e[l[k]],l[k]-1); e[l[k]]+=e[k]; q.emplace(e[l[k]],l[k]-1); } if(r[k]!=1){ re.emplace(e[r[k]],r[k]-1); e[r[k]]+=e[k]; q.emplace(e[r[k]],r[k]-1); } l[r[k]]=l[k],r[l[k]]=r[k]; //2步合起来就是修改 } int main(){ ios::sync_with_stdio(false); cin.tie(0); init(); cin>>n>>k; //md idx与i对应好好的 idx=i+1 没变动 不需要fidx[]来记录 for(int i=1;i<=n;i++){ int x; cin>>x; insert(l[1],x); q.emplace(x,i); } while(k--){ while(re.size()&&re.top()==q.top())re.pop(),q.pop(); auto [x,id]=q.top(); q.pop(); remove(id+1); } for(int i=r[0];i!=1;i=r[i])cout<<e[i]<<' '; return 0; }
I.景区导游
写了floyd暴力,不知道是否正确。
正解待补---lca。
J.砍树
输出样例。。。
正解待补---lca。