HRBU 2021暑期训练解题报告Day3

目录

A - Bombing

B - Constructing the Array

C - 士兵队列训练问题

D - Anagram

E - Fence Repair

F - Black Box

G - 迷宫问题


A - Bombing

题目:(改编版翻译,中心题意不变)

你现在沉迷于瘟疫公司这款游戏,现在游戏推出了新模式,你迫不及待地打开了游戏。
现在,在一个1e9×1e9的平面图上有n座城市(n<=100000),系统已经为你提供了它们的坐标。
你有m架轰炸机,每架轰炸机上都携带着病毒炸弹。而每一架轰炸机的飞行模式为c d。

  1. 当c=0时,轰炸机将会朝着x=d的方向轰炸城市;
  2. 当c=1时,轰炸机将会朝着y=d的方向轰炸城市。

现在你想知道你的每一架轰炸机摧毁了多少城市。
(当城市A被摧毁时,城市A不能再被记入后面的轰炸机战绩之中)

思路:

设计map容器存储数据:

map<int,multiset<int> > mx,my;

mx[x坐标]=>{y1,y2,y3...}  落在x坐标上的y坐标集合

my[y坐标]=>{x1,x2,x3...}  落在y坐标上的x坐标集合

学过数据库系统的同学就会对这题的中心更加理解:删除一条属性时首先去除它的参照完整性。
具体看代码。

  • 考察点:可重复元素集合(multiset),映射对(Map),STL,思维

  • 坑点:已被摧毁的城市不能再被计入战绩之中

代码:

#include<iostream>
#include<queue>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
int main(){
    int n,m;
    ios::sync_with_stdio(false);
    while(cin>>n>>m&&(n+m)!=0){
        map<int,multiset<int> > mx,my;
        multiset<int>::iterator it;
        int x,y,c,d;
        for(int i=0;i<n;i++){
            cin>>x>>y;
            mx[x].insert(y);
            my[y].insert(x);
        }
        for(int i=0;i<m;i++){
            cin>>c>>d;
            int bomb=0;
            if(!c){
                bomb=mx[d].size();
                for(it=mx[d].begin();it!=mx[d].end();it++){
                    my[*it].erase(d);
                }
                mx[d].clear();
            }
            else{
                bomb=my[d].size();
                for(it=my[d].begin();it!=my[d].end();it++){
                    mx[*it].erase(d);
                }
                my[d].clear();
            }
            cout<<bomb<<endl;
        }
        cout<<endl;
    }
}

B - Constructing the Array

题意:

给定一个长为n的空数组,将1~n的数字放到数组之中。

放置规则:

  1. 选择长度最长的空白元素区间,如果有多个,选择最靠左的区间;
  2. 将 i 放置在 (l+r)/2 的位置;

输出最后的数组。

思路:

第一种:拿数字找位置(二分+优先队列)

将空元素区间存储在优先队列之中并设计排序:

struct node
{
    int l,r;
    node() {}
    node(int ll,int rr):l(ll),r(rr) {}
};
struct cmp
{
    bool operator()(node a,node b)
    {
        if(a.r-a.l!=b.r-b.l)
            return (a.r-a.l)<(b.r-b.l);
        else
            return a.l>b.l;
    }
};
priority_queue<node,vector<node>,cmp > q;

二分处理问题,模拟全过程即可。

  • 考察点:优先队列(Priority queue),二分,数据结构,模拟,思维

代码:

#include<iostream>
#include<queue>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
const int maxn=2e5+100;
int ans[maxn];
struct node
{
    int l,r;
    node() {}
    node(int ll,int rr):l(ll),r(rr) {}
};
struct cmp
{
    bool operator()(node a,node b)
    {
        if(a.r-a.l!=b.r-b.l)
            return (a.r-a.l)<(b.r-b.l); 
        else
            return a.l>b.l;
    }
};
priority_queue<node,vector<node>,cmp > q;
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        cin>>n;
        q.push(node(1,n));
        int cnt=1;
        while(!q.empty())
        {
            node tmp=q.top();
            q.pop();
            int mid=(tmp.l+tmp.r)/2;
            ans[mid]=cnt++;
            if(mid-1>=tmp.l)
                q.push(node(tmp.l,mid-1));
            if(mid+1<=tmp.r)
                q.push(node(mid+1,tmp.r));
        }
        for(int i=1; i<=n; i++)
        {
            if(i!=1)
                cout<<" ";
            cout<<ans[i];
        }
        cout<<endl;
    }
}

C - 士兵队列训练问题

题意:

中文题唉大哥,都是中国人你问我?

思路:

模拟题,数组、vector都可以。

需要注意的是,如果用的是vector中的erase函数,需要找出每次迭代器需要移动的次数:

  • 当报2时,移动次数为1;
  • 当报3时,移动次数为2;
  • 考察点:思维,模拟

代码:

#include<bits/stdc++.h>
#define inf 0x3f3f3f
using namespace std;
const int maxn=1e5+10;
typedef long long ll;
int a[maxn];
bool b[maxn];
int main()
{
    int n,cnt=0;
    vector<int>q;
    while(cin>>n)
    {
        for(int i=0; i<n; i++)
            cin>>a[i];
        for(int o=0; o<n; o++)
        {
            q.clear();
            for(int i=1; i<=a[o]; i++)
                q.push_back(i);
            vector<int>::iterator it;
            while(q.size()>3)
            {
                cnt=0;
                while(1)
                {
                    cnt=cnt+1;
                    if(cnt>=q.size())
                        break;
                    q.erase(q.begin()+cnt);
                }
//                for(it=q.begin(); it!=q.end(); it++)
//                {
//                    cout<<*it<<' ';
//                }
//                cout<<endl;
                if(q.size()<=3)
                    break;
                cnt=0;
                while(1)
                {
                    cnt=cnt+2;
                    if(cnt>=q.size())
                        break;
                    q.erase(q.begin()+cnt);
                }

//                for(it=q.begin(); it!=q.end(); it++)
//                {
//                    cout<<*it<<' ';
//                }
//                cout<<endl;
//                cout<<"-----------------------------------------------"<<endl;
            }
            cnt=0;
            for(it=q.begin(); it!=q.end(); it++)
            {
                if(cnt)
                    cout<<' ';
                cnt++;
                cout<<*it;
            }
            cout<<endl;
        }
    }
}

D - Anagram

题意:

给出仅含有大写字母与小写字母组成的字符串s,按照题目中给出的字典序大小顺序由小到大输出s的所有排列结果。

思路:

使用stl提供的next_permutation()即可解决问题。

  • 考察点:全排列,字典序,STL

  • 坑点:题目中有给定的字典序

代码:

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
bool cmp(char a,char b){
    if(a<='z'&&a>='a'){
        if(b<='z'&&b>='a') return a<b;
        else return a<(b+32);
    }
    else{
        if(b<='z'&&b>='a') return (a+32)<=b;
        else return a<b;
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        char s[20];
        scanf("%s",s);
        int len=strlen(s);
        sort(s,s+len,cmp);
        do{
            printf("%s\n",s);
        }while(next_permutation(s,s+len,cmp));
    }
    return 0;
}

E - Fence Repair

题意:(改编版翻译,题意不变)

这里有n个木板,你请了一个木匠把它们接在一起去。
当木匠将一块长为a的木板与一块长为b的木板接成一块长为(a+b)的木板时,他会收取(a+b)元钱。

你也不是什么大户人家,请求出你需要把所有木板接在一起所要支付的最小价格。

思路:

每次都将当前最短的两个木板接起来就可以了。(哈夫曼树)

  • 考察点:优先队列(Priority queue),哈夫曼树,贪心

  • 坑点:数据有点大,开longlong

代码:

#include<iostream>
#include<queue>

using namespace std;
typedef long long ll;
int main(){
    int n,x;
    cin>>n;
    priority_queue<int,vector<int>,greater<int> > p;
    for(int i=0;i<n;i++){
        cin>>x;
        p.push(x);
    }
    ll ans=0;
    while(p.size()>=2){
        ll a=p.top();
        p.pop();
        ll b=p.top();
        p.pop();
        ans+=(a+b);
        p.push(a+b);
    }
    cout<<ans<<endl;
}

F - Black Box

题意:

给定一个长为n的序列a,并有m次询问。

第i次询问的内容为:由序列a的前xi个数字组成的新序列里第i大的数字为多少?

保证每次的询问合理并且 x[i-1]<=x[i]。

思路:

与Day2的C题有着异曲同工之妙,对顶堆问题。

设计两个优先队列:

priority_queue<int> big;   //大根堆,存储前i-1个数
priority_queue<int,vector<int>,greater<int> > small;  //小根堆,存储第i~第xi个数

模拟整个过程,期间维护:当我们需要求第i大的数时,big.size()==i-1。

  • 考察点:优先队列(Priority queue),对顶堆,数据结构,STL

代码:

#include<iostream>
#include<queue>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
int a[30500];
int main()
{
    int n,m,x;
    while(cin>>n>>m)
    {
        priority_queue<int> big;
        priority_queue<int,vector<int>,greater<int> > small;
        for(int i=0; i<n; i++)
            cin>>a[i];
        int pos=0;
        for(int i=1; i<=m; i++)
        {
            cin>>x;
            while(pos<x)
            {
                small.push(a[pos++]);
                if(!big.empty()&&big.top()>small.top())
                {
                    int x=big.top();
                    big.pop();
                    small.push(x);

                    int y=small.top();
                    small.pop();
                    big.push(y);
                }
            }
            int ans=small.top();
            cout<<ans<<endl;
            small.pop();
            big.push(ans);
        }
    }
}

G - 迷宫问题

  • 考察点:队列(Queue),栈(Stack),BFS,递归 

题目讲解与代码:

点我康点好康的[doge]

猜你喜欢

转载自blog.csdn.net/qq_45750296/article/details/119426704