目录
A - Bombing
题目链接:HRBU 2021年暑期训练Day3 - Virtual Judge
题意:在一个二维平面上给你几个点,然后给你几条直线,问每一条直线穿过了几个点
图示
根据图示,我们可以解释清楚样例是个怎么意思,首先三个点的位置即是红色的交叉,第一条直线是x=1通过两个点,第二条直线是y=3通过两个点,但是由于第一个点已经被上一条直线通过了,所以不算作次数,因此答案输出为2 1
思路:把每一个点的横纵坐标分开存储,然后根据每次输入的直线的方向去选择是用横坐标数组还是纵坐标数组,确定数组之后,接着确定要去遍历的范围,然后给满足条件的坐标进行标记,下一次再遍历到这个点时直接跳过不计数。两点需要注意的:1、存储横纵坐标的数据推荐使用结构体,方便之后的标记(要是有别的好方法,当我没说) 2、确定遍历范围的查找方法推荐二分,因为数据范围并不是非常友好
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<string> #include<string.h> #include<cstdlib> #include<fstream> #include<queue> #include<stack> #include<map> #include<set> using namespace std; typedef long long ll; const int maxn=1e5+10; struct Node { int num; int pos; }ex[maxn],ey[maxn]; bool vis[maxn]; bool cmp(Node a,Node b) { return a.num<b.num; } int main() { int N,M; while(scanf("%d%d",&N,&M)!=EOF) { int ans=0; if(N==0&&M==0) break; for(int i=0;i<N;i++) { int x,y; scanf("%d%d",&x,&y); ex[i].num=x; ex[i].pos=i; ey[i].num=y; ey[i].pos=i; vis[i]=0; } sort(ex,ex+N,cmp); sort(ey,ey+N,cmp); for(int i=0;i<M;i++) { ans=0; Node *now; int c,tmp; scanf("%d%d",&c,&tmp); if(c) now=ey; else now=ex; int l=0,r=N-1,mid; mid=(l+r)/2; while(l<=r) { if(now[mid].num<tmp) l=mid+1; else r=mid-1; mid=(l+r)/2; } for(int j=l;j<N;j++) { if(now[j].num!=tmp) break; if(vis[now[j].pos]) continue; vis[now[j].pos]=true; ans++; } printf("%d\n",ans); } printf("\n"); } return 0; }
B - Constructing the Array
题目链接:HRBU 2021年暑期训练Day3 - Virtual Judge
题意:给你一种方法,要求你把n个数字按照给定的方法塞入数组,并且输出数组
图示
图中举例了1~5的插值方法,就是首先判断区间两端相加是否是偶数,是偶数则直接除就行,否则两端和还得-1才能进行除操作,其次是区间两端的选择优先,优先左边区间且长度更大
思路:通过图示,我们需要实现的操作就是如何去选择这个区间,我们需要一个能够帮助我们进行自动排序的容器,且自己定义一下排序规则,非优先队列莫属了。重载好排序规则后,我们需要做的操作只有判断端点和的奇偶性以及下一次放入队列的数据就好啦
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<string> #include<string.h> #include<cstdlib> #include <fstream> #include<queue> #include<stack> #include<map> #include<set> using namespace std; typedef long long ll; const int maxn=2e5+10; int t,n; struct Node { int l,r,len; bool operator < (const Node&tmp) const { if(this->len==tmp.len) return this->l>tmp.l; return this->len<tmp.len; } }; int arr[maxn]; int main() { cin>>t; while(t--) { priority_queue<Node> Q; cin>>n; Q.push((Node){1,n,n}); int i=0; while(i!=n) { Node now=Q.top(); Q.pop(); int l=now.l,r=now.r,mid; if((r-l+1)%2==1) arr[(l+r)/2]=++i,mid=(l+r)/2; else arr[(l+r-1)/2]=++i,mid=(l+r-1)/2; if(l<=mid-1) Q.push((Node){l,mid-1,(mid-1)-l+1}); if(mid+1<=r) Q.push((Node){mid+1,r,r-(mid+1)+1}); } cout<<arr[1]; for(int i=2;i<=n;i++) cout<<" "<<arr[i]; cout<<endl; } return 0; }
C - 士兵队列训练问题
题目链接:HRBU 2021年暑期训练Day3 - Virtual Judge
题意:中文!自己康康
图示
具体就是这么一个情况,当队伍中人数大于三个人时,就继续进行报数,奇数次时就报1,2报到2号的出列,偶数次时就报1,2,3报到三号的出列,以此循环最后得到答案
思路:整一个队列!队内元素个数大于三个时,就一直循环,奇数次就1,2循环,抛出报2的;偶数次就1,2,3循环,抛出报3的
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<string> #include<string.h> #include<cstdlib> #include <fstream> #include<queue> #include<stack> #include<map> #include<set> using namespace std; typedef long long ll; const int maxn=5e3+10; int t,N,num[maxn]; int main() { cin>>t; while(t--) { queue<int> Q; scanf("%d",&N); for(int i=1;i<=N;i++) Q.push(i); int cnt=1; while(Q.size()>3) { int x=Q.size(); if(cnt%2!=0) { for(int i=0;i<x/2;i++) { Q.push(Q.front()); Q.pop(); Q.pop(); } if(x%2!=0) { Q.push(Q.front()); Q.pop(); } } else { for(int i=0;i<x/3;i++) { Q.push(Q.front()); Q.pop(); Q.push(Q.front()); Q.pop(); Q.pop(); } while(x%3!=0) { --x; Q.push(Q.front()); Q.pop(); } } cnt++; } while(Q.size()!=1) { cout<<Q.front()<<" "; Q.pop(); } cout<<Q.front()<<endl; } return 0; }
D - Anagram
题目链接:HRBU 2021年暑期训练Day3 - Virtual Judge
题意:给你一个字符串,要求你输出这个字符串的所有排列方式
思路:没啥思路,唯一的坑点就是这个排列方式分大小,而且相同字母,大写字母小于小写字母,不同字母就是按照正常字典序,注意这一点即可,然后再引入一个全排列的函数next_permutation函数介绍推荐下方博客(因为自己实在说不明白)以及使用时需要与do while一起
博客:全排列 next_permutation() 函数的用法 - 白雪儿 - 博客园
#include<iostream> #include<algorithm> #include<queue> #include<cstring> #include<string> #include<cstdio> using namespace std; typedef long long ll; typedef unsigned long long ull; const int maxx=1e7+10; const int maxn=1e5+10; bool cmp(char a,char b) { if(a>='A'&&a<='Z'&&b>='A'&&b<='Z') return a<b; if(a>='A'&&a<='Z'&&b>='a'&&b<='z') return (a+32)<=b; if(a>='a'&&a<='z'&&b>='a'&&b<='z') return a<b; if(a>='a'&&a<='z'&&b>='A'&&b<='Z') return a<(b+32); } int main() { int T; char s[15]; scanf("%d",&T); while(T--) { scanf("%s",s); int l=strlen(s); sort(s,s+l,cmp); do { printf("%s\n",s); } while(next_permutation(s,s+l,cmp)); } return 0; } /* 4+3+2+1=10 */
E - Fence Repair
题目链接:HRBU 2021年暑期训练Day3 - Virtual Judge
题意:给你n个需要的木板长度,你需要冲n个已知木板长度的总和中截取出这n个数据,每次截取需要消耗一个值(一会放在样例里解释)问最小的花费是多少?
图示
木板的总和是所有需要的长度之和,这是已知的。第一次需要8长度,所以从21中截取出8
cost:21 get:13
第二次从13中截取出5
cost:21+13 get:8
因为已经只剩8了刚刚好满足第三次,所以第三次就不用在截取了所以总消费:21+13=34
思路:我们需要做到的,只需要一直计算出当前的最大值然后加去次大值即可,一开始拿最初的想法,拿数组去存数据,接着每次都进行排序,减去最后一位最大的,然后得出的答案,可想而知,直接T了。血与泪的教训(QAQ自己知道就好,没必要尝试)优先队列真是个好东西!自己重载规则后,循环遍历就好了
#include<iostream> #include<algorithm> #include<queue> using namespace std; typedef long long ll; typedef unsigned long long ull; const int maxx=1e7+10; const int maxn=2e5+10; ll N,L; struct cmp { bool operator () (long long int &a,long long int &b) { return a > b; } }; /*bool cmp(ll x,ll y) { return x>y; }*/ int main() { /*while(cin>>N) { ll sum=0,ans; for(int i=1;i<=N;i++) cin>>L[i]; sort(L+1,L+N+1,cmp); ans=0; while(N!=1) { L[N-1]+=L[N]; N--; ans+=L[N]; sort(L+1,L+N+1,cmp); } cout<<ans<<endl; }*/ while(cin>>N) { ll ans=0; priority_queue<ll,vector<ll>,cmp> Q; for(int i=1;i<=N;i++) cin>>L,Q.push(L); while(Q.size()>1) { ll a=Q.top(); Q.pop(); ll b=Q.top(); Q.pop(); Q.push(a+b); ans+=a+b; } cout<<ans<<endl; } return 0; } /* 4+3+2+1=10 */
F - Black Box
题目链接:HRBU 2021年暑期训练Day3 - Virtual Judge
题意:提供一个数组,要求你输出在u[i]范围内第i小的数字
思路:第二天引入的对顶堆想法,把数组进行分隔存储到两个堆里,因为需要的是小的数字,所以每一次进行数据分堆时,保证小根堆堆顶的数据就是我们寻找的数据即可,其次题目保证了,数据范围是递增的,不需要担心数组还会缩回去这个问题。
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<string> #include<string.h> #include<cstdlib> #include <fstream> #include<queue> #include<stack> #include<map> #include<set> using namespace std; typedef long long ll; const int maxn=3e5+10; int t,n; int main() { int M,N,A[maxn],x; priority_queue<int,vector<int>,less<int> > B; priority_queue<int,vector<int>,greater<int> > S; int cnt=0; cin>>N>>M; for(int i=0;i<N;i++) cin>>A[i]; for(int i=1; i<=M; i++) { cin>>x; while(cnt<x) { S.push(A[cnt]); if(!B.empty()&&S.top()<B.top()) { int tmp=S.top(); S.pop(); B.push(tmp); S.push(B.top()); B.pop(); } cnt++; } cout<<S.top()<<endl; B.push(S.top()); S.pop(); } return 0; }
G - 迷宫问题
题目链接:HRBU 2021年暑期训练Day3 - Virtual Judge
题意:给定一个迷宫,迷宫中只有0,1两个数据,0代表可以通行,1代表不能通行,要求输出从左上角到右下角的移动路径
思路:标准迷宫问题,BFS板子题,输出稍微注意一下就行
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<string> #include<string.h> #include<cstdlib> #include<fstream> #include<queue> #include<stack> #include<map> #include<set> using namespace std; typedef long long ll; const int maxn=6; int mp[maxn][maxn]; bool vis[maxn][maxn]; int sx,sy,ex,ey; int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}}; struct Node { int x,y; }; Node ans[maxn][maxn]; void bfs() { queue<Node> Q; Node P; P.x=sx,P.y=sy; vis[sx][sy]=true; Q.push(P); while(!Q.empty()) { Node now=Q.front(); Q.pop(); for(int i=0;i<4;i++) { Node tmp; tmp.x=now.x+dir[i][0]; tmp.y=now.y+dir[i][1]; if(tmp.x>=0&&tmp.x<5&&tmp.y>=0&&tmp.y<5&&!vis[tmp.x][tmp.y]&&mp[tmp.x][tmp.y]!=1) { vis[tmp.x][tmp.y]=true; ans[tmp.x][tmp.y]=now; Q.push(tmp); } if(tmp.x==ex&&tmp.y==ey) return; } } } void print(int x,int y) { if(x==sx&&y==sy) cout<<"("<<sx<<", "<<sy<<")"<<endl; else { print(ans[x][y].x,ans[x][y].y); cout<<"("<<x<<", "<<y<<")"<<endl; } } int main() { sx=0,sy=0,ex=4,ey=4; for(int i=0;i<5;i++) { for(int j=0;j<5;j++) cin>>mp[i][j]; } memset(vis,false,sizeof(vis)); bfs(); print(4,4); return 0; }