ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

刷洛谷题单【数据结构2-1】

2022-07-26 05:31:53  阅读:121  来源: 互联网

标签:std 刷洛谷题 果子 int namespace 数据结构 using include


P1090 合并果子

题目描述

在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。

每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 \(n-1\) 次合并之后, 就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。

因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 \(1\) ,并且已知果子的种类 数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。

例如有 \(3\) 种果子,数目依次为 \(1\) , \(2\) , \(9\) 。可以先将 \(1\) 、 \(2\) 堆合并,新堆数目为 \(3\) ,耗费体力为 \(3\) 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 \(12\) ,耗费体力为 \(12\) 。所以多多总共耗费体力 \(=3+12=15\) 。可以证明 \(15\) 为最小的体力耗费值。

试做

代码

#include<iostream>
#include<queue>
using namespace std;
priority_queue<long long,vector<long long>,greater<long long>>q;
long long total=0;
int main() {
    int t; cin >> t; 
    long long x;
    for(int i=0;i<t;i++) {
        cin >> x;
        q.push(x);
    }
    long long a, b;
    for (int i = 0; i < t - 1; i++) {
        a = q.top(); q.pop();
        b = q.top(); q.pop();
        total += a + b;
        q.push(a+b);
    }
    cout << total;
}

批改

\(AC\)了

他山之石

最高赞题解,讲了一些c++模板知识

优先队列与堆

P3865 【模板】ST 表

题目背景

这是一道 ST 表经典题——静态区间最大值

请注意最大数据时限只有 0.8s,数据强度不低,请务必保证你的每次查询复杂度为 \(O(1)\)。若使用更高时间复杂度算法不保证能通过。

如果您认为您的代码时间复杂度正确但是 TLE,可以尝试使用快速读入:

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
	return x*f;
}

函数返回值为读入的第一个整数。

快速读入作用仅为加快读入,并非强制使用。

题目描述

给定一个长度为 \(N\) 的数列,和 $ M $ 次询问,求出每一次询问的区间内数字的最大值。

image

用我自己话说,ST 算法就是从两个端点分别相向出发,快速找到不超过另一个端点的大区间(至少超过一半)。合并两个区间答案就能得到查询区间答案(是因为max的特性:不能漏,但可以重)。

加速找区间的方法是倍增。ST算法本质是区间\(\rm dp\)。

看到了一个总结RMQ问题的文章:浅析RMQ问题

板子

引用自:https://www.luogu.com.cn/blog/attack/solution-p3865

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN=1e6+10;
inline int read()
{
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
int Max[MAXN][21];
int Query(int l,int r)
{
    int k=log2(r-l+1); 
    return max(Max[l][k],Max[r-(1<<k)+1][k]);//把拆出来的区间分别取最值 
}
int main()
{
    #ifdef WIN32
    freopen("a.in","r",stdin);
    #endif
    int N=read(),M=read();
    for(int i=1;i<=N;i++) Max[i][0]=read();
    for(int j=1;j<=21;j++)
        for(int i=1;i+(1<<j)-1<=N;i++)//注意这里要控制边界 
            Max[i][j]=max(Max[i][j-1],Max[i+(1<<(j-1))][j-1]);//如果看不懂边界的话建议好好看看图 
    for(int i=1;i<=M;i++)
    {
        int l=read(),r=read();
        printf("%d\n",Query(l,r));
    }
    return 0;
}

P1878 舞蹈课

题目描述

有 \(n\) 个人参加一个舞蹈课。每个人的舞蹈技术由整数来决定。在舞蹈课的开始,他们从左到右站成一排。当这一排中至少有一对相邻的异性时,舞蹈技术相差最小的那一对会出列并开始跳舞。如果不止一对,那么最左边的那一对出列。一对异性出列之后,队伍中的空白按原顺序补上(即:若队伍为 ABCD,那么 BC 出列之后队伍变为 AD)。舞蹈技术相差最小即是 \(a_i\) 的绝对值最小。

任务是模拟以上过程,确定跳舞的配对及顺序。

试做

思路

想法是用优先队列。

算法第一部分:扫描所有相邻的BG,记录id、数值,入队。

算法第二部分:

  1. 取队头,存入答案队列,标记\(vis\),然后按\(id\)前后搜索是否因为这一对出队,出现了新的BG。如果有,入队。

  2. 之后继续取队头,如果取出的元素已经vis,跳过。

算法第三部分:打印答案序列

代码

#include "iostream"
#include <bits/stdc++.h>
using namespace std;
#define N 20010
struct Couple{
    int id1,id2;
    int a,b;
    Couple(int id_1,int id_2,int aa,int bb){
        id1=min(id_1,id_2);
        id2=max(id_1,id_2);
        a=min(aa,bb);
        b=max(aa,bb);
    }
    bool operator<(const Couple &c)const {
        return b-a>c.b-c.a;
    }
    void print()const{
        cout<<id1<<' '<<id2<<endl;
    }
};
priority_queue<Couple>q;//优先队列 用于处理数据
vector<Couple>qq;//答案序列
char str[N];
int num[N];bool vis[N];
int main(){
    int t;cin>>t;
    cin>>str+1;
    for(int i=1;i<=t;i++)cin>>num[i];
    for(int i=2;i<=t;i++){
        if(str[i]!=str[i-1]){
            q.push(Couple(i,i-1,num[i],num[i-1]));
        }
    }
    while(!q.empty()){
        Couple temp=q.top();
        q.pop();
        int t1=temp.id1,t2=temp.id2;
        
        if(vis[t1]||vis[t2])continue;
        vis[t1]=vis[t2]=true;
        qq.push_back(temp);
        
        while(vis[t1])t1--;
        while(vis[t2])t2++;
        
        if(str[t1]!=str[t2]){//新的Couple
            q.push(Couple(t1,t2,num[t1],num[t2]));
        }
    }
    cout<<qq.size()<<endl;
    for(Couple c:qq){
        c.print();
    }
}

批改

image

分析

。。。不给下载样例我也不好分析,看题解罢

这篇题解跟我想得一模一样啊为什么我错了qwq

他山之石

https://www.luogu.com.cn/blog/qqq1112/solution-p1878

//代码里的register均可省去,是用于优化的 
#include <bits/stdc++.h>
using namespace std;
pair < int, int > ans[1000001];//定义pair来存答案 
int n, q[1000001], z;//z是答案总数 
string s;
bool vis[1000001], f[1000001];//vis是用来判断是否选过这个人,f数组表示是男是女(男是true,女是false) 

struct node
{
    int x, y, z;//x为其中一个舞者的编号,y为另一个,z为舞技之差
    friend bool operator < (node a, node b)
    {
        if(a.z == b.z)
        {
            return a.x > b.x;//priority_queue默认大根堆,要反过来写 
        }
        else
        {
            return a.z > b.z;//同理 
        }
    }
};//不用定义结构体数组 
priority_queue < node, vector < node > > pru;//加不加vector都一样,加能更快一些 
int main()
{
    scanf("%d", &n);
    cin >> s;
    for(register int i = 0; i < n; ++i)
    {
        if(s[i] == 'B')//用f数组表示男女 
        {
            f[i + 1] = true;//要加一(string是从0 ~ n - 1的) 
        }
        else//不是男就是女
        {
            f[i + 1] = false;
        }
    }
    for(register int i = 1; i <= n; ++i)
    {
        scanf("%d", &q[i]);
    }
    for(register int i = 1; i < n; ++i)
    {
        if(f[i] != f[i + 1])//不能男和男跳舞,女和女跳舞 
        {
            pru.push((node){i, i + 1, abs(q[i] - q[i + 1])/*要加abs*/});//可以把它们强制转化到一个结构体里 
        }
    }
    while(!pru.empty())
    {
        int x = pru.top().x;//取出第一个人来 
        int y = pru.top().y;//取出第二个人来 
        pru.pop();//pop掉 
        if(!vis[x] && !vis[y])//不能有一个人跳过舞了 
        {
            vis[x] = true;//改成跳过了 
            vis[y] = true;
            ans[++z].first = x;//存答案 
            ans[z].second = y;//z只加一遍 
            //再找一对儿相邻的入队
            //x向左边,y向右边(因为x小,y大) 
            while(x > 0 && vis[x])//跳过了或小于零都不行 
            {
                --x;//往左边 
            }
            while(y <= n && vis[y])//跳过了或大于n都不行 
            {
                ++y;//往右边 
            }
            if(x > 0 && y <= n/*有点多余*/ && f[x] != f[y])//不能男和男或女和女啊 
            {
                pru.push((node){x, y, abs(q[x] - q[y])});//强制转化成结构体再入队 
            }
        }
    }
    printf("%d\n", z);//先输出总数 
    for(register int i = 1; i <= z; ++i)
    {
        printf("%d %d\n", ans[i].first, ans[i].second);//输出舞者 
    }
    return 0;
}

P2251 质量检测

题目描述

为了检测生产流水线上总共 \(N\) 件产品的质量,我们首先给每一件产品打一个分数 \(A\) 表示其品质,然后统计前 \(M\) 件产品中质量最差的产品的分值 \(Q[m] = min\{A_1, A_2, ... A_m\}\),以及第 2 至第 \(M + 1\) 件的 $Q[m + 1], Q[m + 2] $... 最后统计第 \(N - M + 1\) 至第 \(N\) 件的 \(Q[n]\)。根据 \(Q\) 再做进一步评估。

请你尽快求出 \(Q\) 序列。

试做

\(ST\)的裸题

代码

#include "iostream"
#include <bits/stdc++.h>
using namespace std;
#define N 1000010
int n,m,k;
int num[N];
int st[N][21];
int main(){
    cin>>n>>m;
    k=log2(m);
    for(int i=1;i<=n;i++){
        cin>>num[i];
        st[i][0]=num[i];
    }

    for(int j=1;j<=k;j++){
        for(int i=1;i+(1<<j)-1<=n;i++){
            st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
        }
    }

    for(int i=1;i<=n-m+1;i++){
        cout<<min(st[i][k],st[i+m-(1<<k)][k])<<endl;
    }
    return 0;
}

批改

\(A\)了

P1801 黑匣子

题目描述

Black Box 是一种原始的数据库。它可以储存一个整数数组,还有一个特别的变量 \(i\)。最开始的时候 Black Box 是空的.而 \(i=0\)。这个 Black Box 要处理一串命令。

命令只有两种:

  • ADD(x):把 \(x\) 元素放进 Black Box;

  • GET:\(i\) 加 \(1\),然后输出 Black Box 中第 \(i\) 小的数。

记住:第 \(i\) 小的数,就是 Black Box 里的数的按从小到大的顺序排序后的第 \(i\) 个元素。

我们来演示一下一个有11个命令的命令串。(如下表所示)

序号 操作 \(i\) 数据库 输出
1 ADD(3) \(0\) \(3\) /
2 GET \(1\) \(3\) \(3\)
3 ADD(1) \(1\) \(1,3\) /
4 GET \(2\) \(1,3\) \(3\)
5 ADD(-4) \(2\) \(-4,1,3\) /
6 ADD(2) \(2\) \(-4,1,2,3\) /
7 ADD(8) \(2\) \(-4,1,2,3,8\) /
8 ADD(-1000) \(2\) \(-1000,-4,1,2,3,8\) /
9 GET \(3\) \(-1000,-4,1,2,3,8\) \(1\)
10 GET \(4\) \(-1000,-4,1,2,3,8\) \(2\)
11 ADD(2) \(4\) \(-1000,-4,1,2,2,3,8\) /

现在要求找出对于给定的命令串的最好的处理方法。ADD 命令共有 \(m\) 个,GET 命令共有 \(n\) 个。现在用两个整数数组来表示命令串:

  1. \(a_1,a_2,\cdots,a_m\):一串将要被放进 Black Box 的元素。例如上面的例子中 \(a=[3,1,-4,2,8,-1000,2]\)。
  2. \(u_1,u_2,\cdots,u_n\):表示第 \(u_i\) 个元素被放进了 Black Box 里后就出现一个 GET 命令。例如上面的例子中 \(u=[1,2,6,6]\) 。输入数据不用判错。

不会啊,看看题解

他山之石

https://www.luogu.com.cn/blog/119261/solution-p1801,用大小根堆的方法,大佬称之为对顶。

下面是洛谷最高赞的代码,比较简短https://www.luogu.com.cn/blog/Sooke/solution-p1801

#include<cstdio>
#include<queue>
using namespace std;
int a[200005];
int main(){    
    priority_queue<int>A;
    priority_queue<int,vector<int>,greater<int>>B;
    int n,m,r=1,q;scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=m;i++){
        scanf("%d",&q);
        for(int j=r;j<=q;j++){
            A.push(a[j]);
            if(A.size()==i)B.push(A.top()),A.pop();
        }
        r=q+1;
        printf("%d\n",B.top());
        A.push(B.top()),B.pop();
    }
    return 0;
}

P1168 中位数 [绿]

题目描述

给出一个长度为\(N\)的非负整数序列\(A_i\),对于所有\(1 ≤ k ≤ (N + 1) / 2\),输出\(A_1, A_1 \sim A_3, …,A_1 \sim A_{2k - 1}\)的中位数。即前\(1,3,5,…\)个数的中位数。

试做

代码

#include "iostream"
#include <bits/stdc++.h>
using namespace std;
#define N 100010
priority_queue<int,vector<int>>bq;
priority_queue<int,vector<int>,greater<>>sq;
int num[N];
int main(){
    int t;cin>>t;
    for(int i=1;i<=t;i++)cin>>num[i];

    for(int i=1;i<=t;i++){
        if(bq.empty()||num[i]<=bq.top())
            bq.push(num[i]);
        else sq.push(num[i]);
        while (bq.size()-sq.size()>1||bq.size()-sq.size()>1)
            if (bq.size()>sq.size()){
                sq.push(bq.top());
                bq.pop();
            }
            else{
                bq.push(sq.top());
                sq.pop();
            }
        if (i%2) cout<<(bq.size()>sq.size()?bq.top():sq.top())<<endl;
    }
}

批改

\(A\)了。但是奇怪的是abs函数用不了,说是冲突???为毛啊

他山之石

https://www.luogu.com.cn/blog/deco/solution-p1168

#include <bits/stdc++.h>
using namespace std;
int n;
vector<int>a;
int main()
{
    cin>>n;
    for(int i=1,x;i<=n;i++)
    {
        scanf("%d",&x);
        a.insert(upper_bound(a.begin(),a.end(),x),x);//二分插入保证单调性
        if(i%2==1)
        {
        	printf("%d\n",a[(i-1)/2]);//是奇数个就输出
        }
    }
    return 0;
}

emmmm好像是可以用upper_bound过。。。

P2085 最小函数值 [绿]

题目描述

有 \(n\) 个函数,分别为 \(F_1,F_2,\dots,F_n\)。定义 \(F_i(x)=A_ix^2+B_ix+C_i(x\in\mathbb N*)\)。给定这些 \(A_i\)、\(B_i\) 和 \(C_i\),请求出所有函数的所有函数值中最小的 \(m\) 个(如有重复的要输出多个)。

试做

代码

#include "iostream"
#include <bits/stdc++.h>
using namespace std;
#define N 100010
struct Node{
    int id;
    int dir;
    int x;
    int value;
    bool operator>(const Node&node)const{
        return value>node.value;
    }
};
priority_queue<Node,vector<Node>,greater<>>q;
int a[N],b[N],c[N];

int cal(int aa,int bb,int cc,int x){
    return aa*x*x+bb*x+cc;
}

int main(){
    int n,m;cin>>n>>m;
    int value,pos;
    for(int i=1;i<=n;i++){
        cin>>a[i]>>b[i]>>c[i];
        pos=-1.0*b[i]/a[i]/2;
        if(pos>0){
            value=cal(a[i],b[i],c[i],pos);
            q.push((Node){i,1,pos,value});
            q.push((Node){i,-1,pos,value});
        }
        else q.push((Node){i,1,1,cal(a[i],b[i],c[i],1)});
    }
    int cnt=0;
    while(cnt<m){
        Node tmp=q.top();q.pop();
        int x=tmp.x,d=tmp.dir,v=tmp.value,id=tmp.id;
        if(x==0)continue;
        cout<<v<<' ';cnt++;
        if(x==1&&d<0)continue;
        q.push((Node){id,1,x+d,cal(a[id],b[id],c[id],x+d)});
    }
}

批改

image

分析

20220726,年轻人第一次秒掉绿题

不分析了。

P1631 序列合并 [绿]

题目描述

有两个长度都是N的序列A和B,在A和B中各取一个数相加可以得到\(N^2\)个和,求这\(N^2\)个和中最小的N个。

试做

代码

#include "iostream"
#include <bits/stdc++.h>
using namespace std;
#define N 100010
int a[N],b[N];
struct T{
    int i,j;
    long v;
    bool operator>(const T&t)const{
        return v>t.v;
    }
};
priority_queue<T,vector<T>,greater<>>q;
int main(){
    int t;cin>>t;
    for(int i=1;i<=t;i++)cin>>a[i];
    for(int i=1;i<=t;i++)cin>>b[i];
    int cnt=0;
    q.push((T){1,1,a[1]+b[1]});

    while(cnt<t){
        T temp=q.top();q.pop();
        int i=temp.i,j=temp.j,v=temp.v;
        cout<<v<<' ';
        cnt++;
        if(j+1<=t)q.push((T){i,j+1,a[i]+b[j+1]});
        if(i+1<=t)q.push((T){i+1,j,a[i+1]+b[j]});
    }
    cout<<endl;
    return 0;
}

批改

image

分析

这题其实比上一题还要简单。但是注意重复!!!

他山之石

去重堆解摘选:

我们发现对于两个序列中的数,a[i]+b[j]<a[i+1]+b[j],a[i]+b[j]<a[i]+b[j+1];

并且很明显有a[1]+b[1]<a[2]+b[2]<……那么我们将其和(N个)压入优先队列。

只要有 i==j 的点被弹出,我们就把 a[i+1]+b[i] 与a[i]+b[i+1] 压入优先队列,

否则,当 i<j 时,将 a[i]+b[j+1] 压入即可; 当 i>j 时,将 a[i+1]+b[j] 压入。

为什么 i<j 时不压入 a[i+1]+b[j] ?我们考虑到此时 a[i+1]+ b[j-1] 及之前的数字可能依旧在优先队列中并且更优,就不必更新。

#26行之后改为以下代码可AC
if(i==j){
	q.push((T){i+1,j,a[i+1]+b[j]});
	q.push((T){i,j+1,a[i]+b[j+1]});
}
if(i>j&&i+1<=t)q.push((T){i+1,j,a[i+1]+b[j]});
if(i<j&&j+1<=t)q.push((T){i,j+1,a[i]+b[j+1]});

另一种堆解

数组解

二分

大佬们实在是太强了qwq

P4053 建筑抢修 [蓝]

暂留待做

标签:std,刷洛谷题,果子,int,namespace,数据结构,using,include
来源: https://www.cnblogs.com/shanzr/p/16519450.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有