河南萌新联赛2025第(六)场:郑州大学(补题)
文章目录
- D.穿过哈气之门
- E.数字支配
- F.迷宫穿越
- I.外卖大战
- J.凹包
- K. 还在分糖果!
- L.整数商店的购物之旅
- 总结
D.穿过哈气之门
题目传送门:穿过哈气之门
思路:就是利用双指针以及滑动窗口,先利用map来记录窗口内部,是否包含各种元素至少1个,接着利用左右指针往右滑动。
这一题比赛时没看,有点小亏。
代码:
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define ll long long
#define endl '\n'
#define pii pair<ll,ll>
#define fi first
#define se second const ll N=1e6+10;
const ll INF=1e18;
ll n,m;
ll a[N]; void solve()
{cin>>n>>m;for(ll i=1;i<=n;i++)cin>>a[i]; map<ll,ll>m1; ll ans=0; ll l=1,r=2; // 滑动窗口的左右指针,初始窗口为[1,1]m1[a[1]]++; // 初始化窗口,加入第一个元素// 滑动窗口循环,当左指针超过右指针时结束while(l<r){// 扩展右指针,直到窗口包含m种不同元素或到达数组末尾while(m1.size()<m && r<n){m1[a[r]]++; // 将当前元素加入窗口并计数r++; // 右指针右移}// 如果当前窗口包含了m种不同元素if(m1.size()==m){// 计算以当前l为左边界的有效子数组数量// 从r到n的所有位置都可以作为右边界,所以数量是n - r + 1ans += (n - r + 1)+1;}// 移动左指针,缩小窗口m1[a[l]]--; // 减少左指针元素的计数if(m1[a[l]]==0) // 如果该元素计数为0,从map中移除m1.erase(a[l]);l++; // 左指针右移}cout<<ans<<endl;
}signed main()
{IOS; ll t=1; // cin>>t; while(t--)solve(); return 0;
}
E.数字支配
题目传送门:数字支配
思路:就是,例如1235,输出999,特例若为9999或9992直接输出就行,一如既往的wa了十几发,当时,本来就想放弃这一题的,结果最后修改对了,主要一直错在了题目上,不理解什么是字典序等级最大,
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define ll long long
#define endl '\n'
#define pii pair<ll,ll>
#define fi first
#define se second
const ll N=1e6+10;
const ll INF=1e18;
ll a,b,x;
void solve()
{string s;cin>>s;if(s.size()==1&&s[0]-'0'<10){cout<<s;return ;}char a='9';string s1;ll k=0;for(ll i=0;i<s.size()-1;i++){if(s[i]=='9')k++;if(s[i]<='9')s1+=a;elsebreak;}if(s[s.size()-1]<'9'&&k==s.size()-1)s1+=s[s.size()-1];string s2=s;sort(s2.begin(),s2.end());if(s2[0]=='9'){cout<<s<<endl;return ;}cout<<s1<<endl;}
signed main()
{IOS;ll t=1;// cin>>t;while(t--)solve();return 0;
}
F.迷宫穿越
题目传送门:迷宫穿越
思路:利用三维BFS跑一遍,就行,真的非常不擅长搜索,私下写这个题,一直在修改BUG。
代码:
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define ll long long
#define endl '\n'
#define pii pair<ll,ll>
#define fi first
#define se second
const ll N=1e6+10;
const ll INF=1e18;ll n,m,k; // n:行数, m:列数, k:最大可破坏障
bool vis[1100][1100][11]; // 访问标记数组:vis[x][y][t]表示在位置(x,y)使用t次破坏的状态是否已访问
string s[N]; // 存储地图信息// BFS队列中的节点结构
struct node{ll x,y; // 当前位置坐标ll dis; // 到达当前位置的距离(步数)ll k; // 已破坏的障碍物数量
};// 方向数组:表示上下左右四个方向的坐标
ll dx[5][3]={{0,1},{1,0},{-1,0},{0,-1}}; // 右、下、上、左
void solve()
{cin>>n>>m>>k; for(ll i=0;i<n;i++){cin>>s[i];}// 特殊情况处理:如果地图只有1x1if(n==m&&n==1){cout<<0<<endl; // 起点即终点,距离为0return ;}// BFS队列初始化queue<node>p;p.push({0,0,0,0}); // 从起点(0,0)出发,初始距离0,破坏次数0vis[0][0][0]=1; // 标记起点状态已访问// BFS循环while(!p.empty()){node num=p.front(); // 取出队首节点p.pop();ll x=num.x,y=num.y,dis=num.dis,k1=num.k;// 如果到达终点,输出距离并结束if(x==n-1&&y==m-1){cout<<dis<<endl;return ;}// 遍历四个方向for(ll i=0;i<4;i++){// 计算新坐标ll xx=x+dx[i][0],yy=y+dx[i][1];// 检查新坐标是否越界if(xx<0||yy<0||xx>=n||yy>=m)continue;// 计算新的破坏次数ll kk=k1;if(s[xx][yy]=='#') // 如果是障碍物,破坏次数+1kk++;// 检查是否已访问该状态或破坏次数超过限制if(vis[xx][yy][kk]||kk>k)continue;// 标记该状态为已访问并加入队列vis[xx][yy][kk]=1;p.push({xx,yy,dis+1,kk});}}// 如果队列空了仍未到达终点,说明无法到达cout<<-1<<endl;
}
signed main()
{IOS; ll t=1; // cin>>t; while(t--)solve(); return 0;
}
I.外卖大战
题目传送门:外卖大战
思路:就是一个模拟题,直接按照模拟就行
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define ll long long
#define endl '\n'
#define pii pair<ll,ll>
#define fi first
#define se second
const ll N = 1e6 + 10;
const ll INF = 1e18;// 存储用户的最低优惠要求
ll a[N];// 定义结构体 node,用于存储美团、饿了么、京东的状态
// x: 平台当前优惠力度,y: 平台订单数量
struct node {ll x, y;
} m, e, j;void solve() {ll n;cin >> n;for (ll i = 1; i <= n; i++) {cin >> a[i];}// 初始化美团、饿了么、京东的状态:// x 为优惠力度(初始为 2,题目规则未明确说明,需结合题意理解)// y 为订单数量(初始为 0)m.x = 2, e.x = 2, j.x = 2;m.y = 0, e.y = 0, j.y = 0;// 若用户数量 ≤3,直接输出 0 0 0if (n <= 3) {cout << 0 << " " << 0 << " " << 0 << endl;return; }// 记录连续未被选择的次数ll k1 = 0, k2 = 0, k3 = 0;// 从第 4 个用户开始处理for (ll i = 4; i <= n; i++) {// f 标记是否已选择平台,初始为 1(未选择)ll f = 1;// 处理美团平台if (a[i] <= m.x && f) { // 若用户要求 ≤ 美团优惠力度,且未选其他平台f = 0; // 标记为已选择平台m.y++; // 美团订单数 +1m.x++; // 美团优惠力度 +1k1 = 0; // 连续未被选次数重置为 0} else { // 未选择美团k1++; // 连续未被选次数 +1}// 处理饿了么平台if (a[i] <= e.x && f) { // 若用户要求 ≤ 饿了么优惠力度,且未选其他平台f = 0; // 标记为已选择平台e.y++; // 饿了么订单数 +1e.x++; // 饿了么优惠力度 +1k2 = 0; // 连续未被选次数重置为 0} else { // 未选择饿了么k2++; // 连续未被选次数 +1}// 处理京东平台if (a[i] <= j.x && f) { // 若用户要求 ≤ 京东优惠力度,且未选其他平台f = 0; // 标记为已选择平台j.y++; // 京东订单数 +1j.x++; // 京东优惠力度 +1k3 = 0; // 连续未被选次数重置为 0} else { // 未选择京东k3++; // 连续未被选次数 +1}// 若平台连续 3 次未被选择,优惠力度 +2if (k1 == 3) {k1 = 0; // 重置连续次数m.x += 2; // 美团优惠力度 +2}if (k2 == 3) {k2 = 0; // 重置连续次数e.x += 2; // 饿了么优惠力度 +2}if (k3 == 3) {k3 = 0; // 重置连续次数j.x += 2; // 京东优惠力度 +2}}// 输出:美团、饿了么、京东的订单数量cout << m.y << " " << e.y << " " << j.y << endl;
}signed main() {IOS;ll t = 1;// cin >> t;while (t--) {solve();}return 0;
}
J.凹包
题目传送门:凹包
思路:就是一个凸包的模板题:
首先是叉积:
Andrew 算法
步骤 1:数据准备与排序
定义点结构:用struct node存储点的x和y坐标。
输入点集:将所有点存入数组a[N]。
排序点集:按以下规则排序(关键步骤):
先按x坐标升序排列;
若x坐标相同,按y坐标升序排列。
排序的目的是为了按顺序遍历点时,能逐步构建凸包的上下两部分。
步骤 2:构建下凸壳(下部分)
遍历排序后的点,构建凸包的下半部分:
ll top = 0; // 栈顶指针,记录当前凸包顶点数量
for (ll i = 1; i <= n; i++) {// 当栈中至少有2个点,且新点导致“非左转”时,弹出栈顶点while (top > 1 && cross(p[top-1], p[top], a[i]) <= 0) top--;p[++top] = a[i]; // 将当前点加入凸包
}
核心判断:叉积cross
函数cross(u, v, w)计算向量(u-w)与(v-w)的叉积:
若叉积>0:三点构成左转(符合凸包的凸性,保留栈顶);
若叉积<=0:三点构成右转或共线(破坏凸性,需弹出栈顶点)。
这一步确保下凸壳的边始终向左上方弯曲,形成凸包的下半部分。
步骤 3:构建上凸壳(上部分)
从排序后的最后一个点反向遍历,构建凸包的上半部分:
ll m = top; // 记录下凸壳的顶点数
for (ll i = n-1; i >= 1; i--) {// 当栈中顶点数超过下凸壳,且新点导致“非左转”时,弹出栈顶点while (top > m && cross(p[top-1], p[top], a[i]) <= 0) top--;p[++top] = a[i]; // 将当前点加入凸包
}
反向遍历的目的是构建凸包的上半部分,确保边始终向左下方弯曲。
变量m用于区分下凸壳和上凸壳,避免重复处理下凸壳的顶点。
步骤 4:凸包结果
最终数组p[1…top]存储了凸包的所有顶点(首尾点相同,可忽略最后一个点)。
凸包的顶点数为top-1(因为第一个点和最后一个点重合)。
代码通过比较凸包顶点数与原始点数量,判断原始多边形是否为凹多边形(凸包顶点数少于原始点数量时,说明存在凹点)。
解释一下:非左转,意思就是该点在前两个点的右侧,或者共线
代码:
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define ll long long
#define endl '\n'
#define pii pair<ll,ll>
#define fi first
#define se second
const ll N = 1e6 + 10;
const ll INF = 1e18;// 定义结构体 node,存储点的坐标(x, y)
struct node
{double x, y;
} p[N], a[N]; // p: 存储凸包结果;a: 存储输入的原始点// 排序比较函数:先按 x 升序,x 相同时按 y 升序
bool cmd(node x, node y)
{if (x.x == y.x)return x.y < y.y;return x.x < y.x;
}// 计算向量叉积,判断点的转向关系
// u, v, w:三个点,计算 (u - w) 和 (v - w) 的叉积
// 叉积 >0:u 在 v 的逆时针方向;=0:共线;<0:顺时针方向
double cross(node u, node v, node w)
{return (u.x - w.x) * (v.y - w.y) - (u.y - w.y) * (v.x - w.x);
}// 计算两点之间的距离
double dis(node x, node y)
{return sqrt((x.x - y.x) * (x.x - y.x) + (x.y - y.y) * (x.y - y.y));
}
void solve()
{ll n;cin >> n; // 读取多边形顶点数// 读取每个顶点的坐标for (ll i = 1; i <= n; i++){cin >> a[i].x >> a[i].y;}// 按 x 升序、x 相同时 y 升序排序,为凸包做准备sort(a + 1, a + 1 + n, cmd);ll top = 0; // 凸包栈的栈顶指针// 构建下凸壳for (ll i = 1; i <= n; i++){// 当栈中至少有 2 个点,且新点导致栈顶线段“非左转”(叉积 <=0),弹出栈顶while (top > 1 && cross(p[top - 1], p[top], a[i]) <= 0) top--;p[++top] = a[i]; // 将当前点加入凸包栈}ll m = top; // 下凸壳构建完成后,记录栈顶位置// 构建上凸壳for (ll i = n - 1; i >= 1; i--){// 当栈中元素超过下凸壳大小,且新点导致栈顶线段“非左转”(叉积 <=0),弹出栈顶while (top > m && cross(p[top - 1], p[top], a[i]) <= 0) top--;p[++top] = a[i]; // 将当前点加入凸包栈}if (top <=n) cout << "Yes" << endl; // 是凹多边形elsecout << "No" << endl; // 是凸多边形
}
signed main()
{IOS;ll t = 1; // cin >> t;while (t--) {solve();}return 0;
}
K. 还在分糖果!
题目传送门:还在分糖果
思路:通过手写观察,可以发现,就是转化成9进制,然而题目中,说又不能有7,故只需把大于等于7的再加1就行了。
这一题耗了好长时间,当时,一直在算样例,是怎么的出来的,结果算了好长时间,,还是不对,最后又重新看了一下题目,是让求第n个糖果的序号,而我前面一直在算第n个序号有多少个糖果,无语,看出来之后,也是没多长时间就找到规律了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define ll long long
#define endl '\n'
#define pii pair<ll,ll>
#define fi first
#define se second
const ll N=1e6+10;
const ll INF=1e18;
ll a[30];
void solve()
{ll n;cin>>n;ll k=1;ll num=n;ll ans=0;while(num>0){a[k++]=num%9;num/=9;}for(ll i=k-1;i>=1;i--)if(a[i]>=7)cout<<a[i]+1;elsecout<<a[i];cout<<endl;
}
signed main()
{IOS;ll t=1;cin>>t;while(t--)solve();return 0;
}
L.整数商店的购物之旅
题目传送门:整数商店的购物之旅
思路:就是一个二分,但是还有一种做法,就是感觉有点贪心。
这一题也是快wa10发了,才开始前一直错是因为求位数时忘记加1了,导致wa了大概有4发左右,接着加上1之后,又接着wa,这又是因为范围没看见,一直第二个样例过不去,而且当时我就没测第二个样例,直接交了,最后才看见有第二个样例,才发现错在哪,如果没第二个样例的话,还真不一定过。还是审题不认真。
代码:
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define ll long long
#define endl '\n'
#define pii pair<ll,ll>
#define fi first
#define se second
const ll N=1e6+10;
const ll INF=1e18;
ll a,b,x;
ll weishu(ll k)
{ll ans=0;while(k>0){k/=10;ans++;}return ans;
}
bool check(ll k)
{ll len=weishu(k);ll ans=a*k+b*len;if(ans<=x)return true;elsereturn false;
}
void solve()
{cin>>a>>b>>x;ll l=1,r=1e9+1;ll ans=0;while(l<r){ll mid=(l+r)>>1;if(check(mid)){l=mid+1;ans=mid;}else{r=mid;}}cout<<ans<<endl;
}
signed main()
{IOS;ll t=1;// cin>>t;while(t--)solve();return 0;
}
第二种方法:
当时,这一种方法也是错在了没限制范围
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define ll long long
#define endl '\n'
#define pii pair<ll,ll>
#define fi first
#define se second
const ll N=1e6+10;
const ll INF=1e18;
ll a,b,x;
void solve()
{cin>>a>>b>>x;for(ll i=x/a;i>=1;i--){ll len=log10(i)+1;if(a*i+len*b<=x){if(i>1e9)i=1e9;cout<<i<<endl;return ;}}
}
signed main()
{IOS;ll t=1;// cin>>t;while(t--)solve();return 0;
}
总结
发现自己只要错一遍之后,后面还会有2 3 4 5 6 7 8 9…遍,老是容易想歪,算了先保证正确吧,之后再提升正确率。