algorithm_group_Round2题解

Posted algorithmgroup

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了algorithm_group_Round2题解相关的知识,希望对你有一定的参考价值。

A.Binary Tree Traversals(二叉树)

题意:

给出一颗二叉树的先序和中序,求后序

题解:

递归建树,细节不表。

技术图片
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=1014;
struct node  {
    int data;
    node * left;
    node * right;
};
int pre[maxn],in[maxn],T,N,q,x;
void init () {
    fill (pre,pre+maxn,0);
    fill (in,in+maxn,0);
}
node * create (int preL,int preR,int inL,int inR) {
    if (preL>preR) return NULL;
    node * root=new node;
    root->data=pre[preL];
    int k;
    for (k=inL;k<=inR;k++) {
        if (in[k]==pre[preL]) break;
    }
    int numLeft=k-inL;
    root->left=create(preL+1,preL+numLeft,inL,k-1);
    root->right=create(preL+numLeft+1,preR,k+1,inR);
    return root;
} 
int num;
void postOrder (node * root) {
    if (root==NULL) return;
    postOrder (root->left);
    postOrder (root->right);
    printf ("%d",root->data);
    num++;
    if (num<N) printf (" ");
    else printf ("
");
}
int main () {
    while (~scanf ("%d",&N)) {
        num=0;
        for (int i=1;i<=N;i++) scanf ("%d",&pre[i]);
        for (int i=1;i<=N;i++) scanf ("%d",&in[i]);
        node * root=create(1,N,1,N);
        postOrder (root);
    }
    return 0;
} 
View Code

 

B.圆桌问题(简单模拟)

题意:

圆桌上围坐着2n个人。其中n个人是好人,另外n个人是坏人。如果从第一个人开始数数,数到第m个人,则立即处死该人;然后从被处死的人之后开始数数,再将数到的第m个人处死……依此方法不断处死围坐在圆桌上的人。试问预先应如何安排这些好人与坏人的座位,能使得在处死n个人之后,圆桌上围坐的剩余的n个人全是好人。

题解:

用vector模拟这个过程即可。

技术图片
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=1e5+100;
int n,m;
vector<int> vi;
int main () {
    while (~scanf("%d%d",&n,&m)) {
        vi.clear();
        for (int i=0;i<2*n;i++) 
            vi.push_back(i);
        int f=0;
        for (int i=0;i<n;i++) 
            f=(f+m-1)%vi.size(),
            vi.erase(vi.begin()+f);
        int j=0;
        for (int i=0;i<2*n;i++) {
            if (!(i%50)&&i) printf("
");
            if (j<vi.size()&&vi[j]==i) 
                printf("G"),j++;
            else 
                printf("B");
        }
        printf("

");
    }
}
View Code

 

C.看病要排队(优先队列)

题意:

看病要排队这个是地球人都知道的常识 不过经过细心的0068的观察,他发现了医院里排队还是有讲究的。0068所去的医院有三个医生(汗,这么少)同时看病。而看病的人病情有轻重,所以不能根据简单的先来先服务的原则。所以医院对每种病情规定了10种不同的优先级。级别为10的优先权最高,级别为1的优先权最低。医生在看病时,则会在他的队伍里面选择一个优先权最高的人进行诊治。如果遇到两个优先权一样的病人的话,则选择最早来排队的病人。

现在就请你帮助医院模拟这个看病过程。

题解:

三个优先队列实现看病顺序。

技术图片
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<string>
#include<iostream>
using namespace std;
const int maxn=2000;
struct node {
    int lev;
    int t;
    bool operator < (const node &r) const {
        if (lev!=r.lev) 
            return lev<r.lev;
        else 
            return t>r.t;
    }
};
priority_queue<node> q[4];
int N;
int main () {
    while (~scanf("%d",&N)) {
        for (int i=0;i<4;i++)
            while (!q[i].empty()) q[i].pop();
        string s;
        int num=1;
        for (int i=1;i<=N;i++) {
            cin>>s;
            if (s=="IN") {
                int x,y;
                scanf("%d%d",&x,&y);
                q[x].push({y,num});
                num++;
            }
            else {
                int x;
                scanf("%d",&x);
                if (q[x].empty()) {
                    printf("EMPTY
");
                }
                else {
                    printf("%d
",q[x].top().t);
                    q[x].pop();
                }
            }
        }
    }
}
View Code

 

D.Til the Cows Come Home(最短路径)

题意:

zzj很热爱学习,他打算偷偷跑回学校学习,为了多学习他希望可以找最快的路线回到学校。 成都市里有N个(2 <= N <= 1000)个地铁站,编号分别为1..N。zzj家在1号地铁站旁边,四川大学江安校区站是N号地铁站。地铁站之间共有M (1 <= M <= 2000)条双向路径。 zzj现在在1号地铁站,他希望知道到学校最短要多长时间。可以保证zzj能到达学校。忽略zzj在换乘地铁时需要的等待时间

题解:

dijkstra模板题,这里我用的是堆优化的dijkstra算法。

技术图片
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=1010;
const int inf=1e9;
int N,M;
int head[maxn*100];
int tol;
struct node {
    int u,v,w,next;
}edge[maxn*100];
void addedge (int u,int v,int w) {
    edge[tol].u=u;
    edge[tol].v=v;
    edge[tol].w=w;
    edge[tol].next=head[u];
    head[u]=tol++;
}
int d[maxn];
int visit[maxn];
struct qnode {
    int v,w;
    bool operator < (const qnode &r) const {
        return w>r.w;
    }
};
void dijkstra (int s) {
    memset(visit,0,sizeof(visit));
    for (int i=1;i<=N;i++) d[i]=inf;
    priority_queue<qnode> q;
    d[s]=0;
    qnode tmp;
    tmp.v=s,tmp.w=0;
    q.push(tmp);
    while (!q.empty()) {
        tmp=q.top();
        q.pop();
        int u=tmp.v;
        if (visit[u]) continue;
        visit[u]=1;
        for (int i=head[u];i!=-1;i=edge[i].next) {
            int v=edge[i].v;
            if (!visit[v]&&d[v]>d[u]+edge[i].w) {
                d[v]=d[u]+edge[i].w;
                tmp.v=v;
                tmp.w=d[v];
                q.push(tmp);
            }
        }
    }
} 
int main () {
    while (~scanf("%d%d",&M,&N)) {
        memset(head,-1,sizeof(head));
        tol=0;
        for (int i=0;i<M;i++) {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
            addedge(v,u,w);
        }
        dijkstra(1);
        printf("%d
",d[N]);
    }
}
View Code

 

E.Mayer posters(线段树+离散化)

题意:

n(n<=10000) 个人依次贴海报,给出每张海报所贴的范围li,ri(1<=li<=ri<=10000000) 。求出最后还能看见多少张海报。

题解:

建立一颗线段树,如果线段树的节点权值为-1,代表包含了多种颜色。每次区间更新的时候以此为标准向下传递信息,类似于延迟标记。

题目给定范围很大,但只有10000个人不需要那么大,很多空的节点是浪费的,所以这里考虑先做一个离散化(坐标轴上所有点映射到一个比较小的区间内,相对距离不变)

离散化时需要多写一句:

if (pos[i]-pos[i-1]>1) pos[m++]=pos[i-1]+1;

 

可以防止一些点因为离散化不当被完全覆盖。

时间复杂度O(NlogN)。

技术图片
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;
const int maxn=1e5+100;
struct node {
    int l,r,sum;
}segTree[maxn*8];
struct post {
    int l,r;
}p[maxn];
set<int> st;
int pos[maxn*4];
void build (int i,int l,int r) {
    segTree[i].sum=-1;
    segTree[i].l=l;
    segTree[i].r=r;
    if (l==r) return;
    int mid=(l+r)>>1;
    build(i<<1,l,mid);
    build(i<<1|1,mid+1,r); 
}
void push_down (int i) {
    segTree[i<<1].sum=segTree[i<<1|1].sum=segTree[i].sum;
    segTree[i].sum=-1;
}
void update (int i,int l,int r,int val) {
    if (segTree[i].l>=l&&segTree[i].r<=r) {
        segTree[i].sum=val;
        return;
    } 
    if (segTree[i].sum!=-1) push_down(i);
    int mid=(segTree[i].l+segTree[i].r)>>1;
    if (r<=mid) 
        update(i<<1,l,r,val);
    else if (l>mid)
        update(i<<1|1,l,r,val); 
    else {
        update(i<<1,l,mid,val);
        update(i<<1|1,mid+1,r,val);
    }
}
void query (int i) {
    if (segTree[i].sum!=-1) {
        st.insert(segTree[i].sum);
        return;
    } 
    if (segTree[i].l==segTree[i].r) return;
    //int mid=(segTree[i].l+segTree[i].r)>>1;
    query(i<<1);
    query(i<<1|1);
}
int main () {
    int T;
    scanf("%d",&T);
    int N;
    while (T--) {
        scanf("%d",&N);
        st.clear();
        int tot=0;
        for (int i=0;i<N;i++) {
            scanf("%d%d",&p[i].l,&p[i].r);
            pos[tot++]=p[i].l;
            pos[tot++]=p[i].r;
        }
        sort(pos,pos+tot);
        int m=unique(pos,pos+tot)-pos;
        int t=m;
        for (int i=1;i<t;i++) 
            if (pos[i]-pos[i-1]>1) pos[m++]=pos[i-1]+1;
        sort(pos,pos+m);
        build(1,1,m);
        for (int i=0;i<N;i++) {
            int x=lower_bound(pos,pos+m,p[i].l)-pos+1;
            int y=lower_bound(pos,pos+m,p[i].r)-pos+1;
            update(1,x,y,i); 
        }
        query(1);
        printf("%d
",st.size());
    }
}
View Code

 

F.Min Inversion Number(树状数组)

题意:

给定一个由0到n−1组成,长度为n每个元素唯一的序列,可以进行一种操作,把第一个元素放到最后一个位置。求经过若干次操作后的,最小逆序对数。

题解:

统计每个数左边比自己大的数的数量,记为l[i],所有l[i]的和就是初始的逆序对的数量。

然后每把一个数移到末尾,所有比它大的数的数量就是新增的逆序对的数量,所有比它小的数的数量就是减少的逆序对的数量,做N次取最小值就是答案。

时间复杂度O(NlogN)。

技术图片
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn=1e5+100;
ll c[maxn];
ll a[maxn];
int N;
int lowbit (int x) {
    return x&-x;
}
void update (int x) {
    for (int i=x;i<=N;i+=lowbit(i)) c[i]++;
} 
ll getsum (int x) {
    ll ans=0;
    for (int i=x;i;i-=lowbit(i)) ans+=c[i];
    return ans;
}
int main () {
    while (~scanf("%d",&N)) {
        memset(c,0,sizeof(c));
        ll ans=0;
        for (int i=1;i<=N;i++) {
            scanf("%d",&a[i]);
            a[i]+=1;
            ans+=getsum(N)-getsum(a[i]);
            update(a[i]);
        }
        ll Min=ans;
        for (int i=1;i<=N;i++) {
            ans+=N-a[i]-(a[i]-1);
            Min=min(Min,ans);
        }
        printf("%lld
",Min);
    }
}
View Code

 

G.Three Blocks Palindrome (hard version)(树状数组+二分)

题意:

给出一串序列,可以删除任意元素使得剩下的序列满足aaa...abbb...baaa....a的形式,其中开始的a和结尾的a数量要相同,询问可以保留的最大序列长度。

题解:

注意到题目的范围是1~200,所以考虑开一个二维树状数组存储每个数的前缀和。

然后遍历一遍序列,首先可以确定的是只有一个数的子序列一定满足条件,所以先取每个数的前缀和的最大值。

然后遍历到每个数,在剩下的区间里二分,check条件是这个数在整个序列的出现次数减去这个数在二分位置之后的出现次数要大于等于这个数在当前位置之前的出现次数。可以找到最优的位置,记当前位置和二分位置为t1和t2。

然后遍历200个数,找到在位置t1和t2之间出现次数最多的数,这个数量和之前统计的两个数量之和就是这个位置的最大值。(可能有点晦涩难懂,看代码会清楚一点)

技术图片
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
int N;
int a[maxn];
int c[210][maxn];
int lowbit (int x) {
    return x&-x;
}
void update (int t,int x) {
    for (int i=x;i<=N;i+=lowbit(i)) c[t][i]++;
}
int getsum (int t,int x) {
    int ans=0;
    for (int i=x;i;i-=lowbit(i)) ans+=c[t][i];
    return ans;
}
int main () {
    int T;
    scanf("%d",&T);
    while (T--) {
        scanf("%d",&N);
        for (int i=1;i<=200;i++)
            for (int j=1;j<=N;j++) 
                c[i][j]=0;
        for (int i=1;i<=N;i++) {
            scanf("%d",&a[i]);
            update(a[i],i);
        }
        int Max=0;
        for (int i=1;i<=N;++i) {
            int cnt=getsum(a[i],i);
            Max=max(Max,cnt);
            int l=i,r=N;
            int ans=1e9;
            int tot=getsum(a[i],N);
            while (l<=r) {
                int mid=(l+r)>>1;
                if (tot-getsum(a[i],mid)>=cnt) {
                    ans=mid;
                    l=mid+1;
                }
                else {
                    r=mid-1;
                }
            } 
            if (ans==1e9) continue;
            int t=0;
            for (int j=1;j<=200;++j) 
                t=max(t,getsum(j,ans)-getsum(j,i));
            Max=max(Max,t+2*cnt);
        }
        printf("%d
",Max);
    }
}
View Code

 

以上是关于algorithm_group_Round2题解的主要内容,如果未能解决你的问题,请参考以下文章

Tokio Marine & Nichido Fire Insurance Programming Contest 2021(AtCoder Regular Contest 122) 题解(代

寒假代更新计划

ICM Technex 2018 and Codeforces Round #463 (Div. 1 + Div. 2, combined) A Palindromic Supersequence(代

题解Matrix BZOJ 4128 矩阵求逆 离散对数 大步小步算法

Codeforces Round #505 (rated, Div. 1 + Div. 2, based on VK Cup 2018 Final) BWeakened Common Diviso(代

杭电2018多校第六场(2018 Multi-University Training Contest 6) 1001.oval-and-rectangle (HDU6362)-数学期望微积分(示例代(