2022.1.18(一测补题,树的遍历题目,并查集)

由于深夜点了外卖要1:50才到

闲着没事就补一下题目!

题目如下:

题目描述

In one one-dimensional world there are nn platforms. Platform with index kk (platforms are numbered from 1) is a segment with coordinates [(k-1)m,(k-1)m+l][(k−1)m,(k−1)m+l] , and l<ml<m . Grasshopper Bob starts to jump along the platforms from point 00 , with each jump he moves exactly dd units right. Find out the coordinate of the point, where Bob will fall down. The grasshopper falls down, if he finds himself not on the platform, but if he finds himself on the edge of the platform, he doesn't fall down.

输入格式

The first input line contains 4 integer numbers nn , dd , mm , ll ( 1<=n,d,m,l<=10^{6},l<m1<=n,d,m,l<=106,l<m ) — respectively: amount of platforms, length of the grasshopper Bob's jump, and numbers mm and ll needed to find coordinates of the kk -th platform: [(k-1)m,(k-1)m+l][(k−1)m,(k−1)m+l] .

输出格式

Output the coordinates of the point, where the grosshopper will fall down. Don't forget that if Bob finds himself on the platform edge, he doesn't fall down.

题意翻译

题目描述:在一坐标轴上给出n块板子,每个板子所占的空间为[(k-1)m,(k-1)m+l](l<m),一个青蛙从原点0起跳,每次跳d距离远,问最后青蛙会落在哪里(没落在板子上就结束跳跃) 输入:一行四个整数n,d,m,l 输出:一个整数,即青蛙最后的落点 1<=n,d,m,l<=10^6 l<m

Translated by 稀神探女

输入输出样例

输入 #1复制

2 2 5 3

输出 #1复制

4

输入 #2复制

5 4 11 8

输出 #2复制

20

测试的时候由于没看懂这个k是个啥玩意,就没看懂题目,害。

其实题目好像很简单的样子,想清楚这个k就行,应该算是一个数学题,思路草稿:

也就是说,其中n为板子数,d为青蛙弹跳力,m为第一块板子距离原点的距离,l为板子宽度,易得板子与板子间的距离为m-l。 

且题目要我们输出的是青蛙掉落的点。

#include<stdio.h>
int main()
{
    long long n,d,m,l,ans=0;
    scanf("%lld %lld %lld %lld",&n,&d,&m,&l);
    for(int k=1; k<=n; k++)//k代表第k块板子
    {
        if(ans<((k-1)*m))//如果本次青蛙跳跃后达不到板子左边缘则落入水中结束跳跃
            break;
        while(ans<=(k-1)*m+l)//跳的上板子就一直跳,直到跳出了板子右边缘
        {
            ans=(((k-1)*m+l)/d)*d+d;//*d/d是为了取整
        }
    }
    printf("%lld",ans);
    return 0;
}

--------------------------------------------------- -------------------------- --------------------------------------------------

上午先补题 这道题题目如下

题意描述

关于 Codeforces 的网站 king Copa 经常被报道,使得它在要使用网站进行训练和比赛的人之间迅速流行开来。最近, Copa 明白,要征服世界,他需要组织世界 Codeforces 锦标赛。他希望在这次比赛之后之后,最聪明的人将成为被挑选出来成为他的下属,然后征服世界最艰难的部分将会完成。

Codeforces 世界总决赛的最后一轮定于 YYYY 年 MMMM 月 DDDD 日举行,其中 DDDD 是当天的日期, MMMM 是当月的月份, YYYY 是当年的年份的最后两位。Bob 很幸运地能成为来自 Berland 的一名决赛选手。但有一个问题:根据比赛规则,所有参赛者在决赛时必须年满 1818 岁。 Bob 出生于 BYBY 年, BMBM 月,BDBD 日。这个日期记录在他的护照上,他的护照复印件已经寄给了组织者。但是 Bob 了解到,在不同的国家,日期的书写方式是不同的。例如,在美国,先写月份,然后写日期,最后写年份。

鲍勃想知道是否有可能重新排列他出生日期的数字,以便他在 YYYY 年, MMMM 月, DDDD 日那天至少 1818 岁。他看出,在他的祖国,日期写的顺序不一样。请帮帮他。 根据另一个奇怪的规则,合格的参赛者必须与决赛日期出生在同一个世纪。如果决赛当天刚好是参赛者的 1818 岁生日,则他可以参加。

因为我们只考虑从 20012001 年到 20992099 年的决赛年份,所以使用以下规则:如果年份的数字可以被 44 整除,那么年份就是闰年。

输入格式:

第一行包括三个数字 DD,MM,YYDD,MM,YY ,第二行包括三个数字 BD,BM,BYBD,BM,BY ,数据保证两个日期的正确性,并且 BYBY 和 YYYY 保证在 [ 01 ,99 ][01,99] 中。

输出格式:

如果可能通过重新排列出生日期的顺序,让 Bob 在比赛当天至少 1818 岁,则输出 YES 。如果不能,则输出 NO。

输入输出样例

输入 #1复制

01.01.98
01.01.80

输出 #1复制

YES

输入 #2复制

20.10.20
10.02.30

输出 #2复制

NO

输入 #3复制

28.02.74
28.02.64

输出 #3复制

NO

 思路如下图(感觉是用枚举吧)(还挺麻烦的样子)

 喔 对了,最近不是自己学了c++不,所以决定用c++来写写看

一开始写着c++的代码一直交c,导致编译错误

后来发现了又因为判断条件写的太复杂了,可能某个小地方有点错误导致有一个测试点过不了

#include<bits/stdc++.h>
using namespace std;
int dday[14]={0,31,29,31,20,31,30,31,31,30,31,30,31};
int dd,mm,yy;//比赛日期

bool check(int year,int month,int day)//判断是否是个合法日期
{
    int sign = 0;//默认为非闰年
    if(year % 4 == 0)
        sign = 1;//如果是闰年标记一下
    if(month > 12)
        return false;
    if(day > dday[month])
        return false;
    else if(sign == 0 && month == 2 && day>28)
        return false;
    else if(sign == 0 && month == 2 && day>29)
        return false;
    else
        return true;
}
bool adult(int year,int month,int day)//判断是否成年
{
    if(yy - year > 18)
        return true;
    else if(yy - year <=17)
        return false;
    else if(yy - year == 18)
    {
        if(mm - month > 0)
            return true;
        else if(mm - month < 0)
            return false;
        else if(mm - month == 0)
        {
            if(dd-day >= 0)
                return true;
            else
                return false;
        }
    }
    return false;
}
int main()
{
    int bd,bm,by;//bob生日
    scanf("%d.%d.%d %d.%d.%d",&dd,&mm,&yy,&bd,&bm,&by);
    if(check(by,bm,bd)&&adult(by,bm,bd)){
        cout<<"YES";
        return 0;
    }
    if(check(by,bd,bm)&&adult(by,bd,bm)){
        cout<<"YES";
        return 0;
    }
    if(check(bm,by,bd)&&adult(bm,by,bd)){
        cout<<"YES";
        return 0;
    }
    if(check(bm,bd,by)&&adult(bm,bd,by)){
        cout<<"YES";
        return 0;
    }
    if(check(bd,by,bm)&&adult(bd,by,bm)){
        cout<<"YES";
        return 0;
    }
    if(check(bd,bm,by)&&adult(bd,bm,by)){
        cout<<"YES";
        return 0;
    }
    cout<<"NO";
    return 0;
}

后面想想,我可以只把判断满足条件的情况,如何其他情况全部返回false即可,修改之后的代码如下,简洁了不少。 

莫名其妙,这题明明很简单,我弄了好久,一直有一个测试点过不了,估计就是一点点小错误导致的,然后重新打了一遍就过了,害。(注意点和我的错误点都在代码中加了注释)

#include<bits/stdc++.h>
using namespace std;
int dday[13]= {0,31,29,31,30,31,30,31,31,30,31,30,31}; //月份所对应的天数用一个数组储存,方便取用
int dd,mm,yy,bd,bm,by;
bool check(int year,int month,int day)//判断是否是个合法日期
{
    if(month > 12 || month < 1 || day < 1 || day > 31 || year < 1 || year > 99)//题目只保证了 输 入 数 据 中的年不越界
        return false;
    else if(day > dday[month])//包含闰年二月份的判断了
        return false;
    else if(year % 4 !=0 && month == 2 && day>28)
        return false;

    ///如果成功运营到此处说明是个合法日期,在此基础上接着判断是否成年

    //判断是否成年
    if(yy - year > 18)
        return true;
    else if(yy - year == 18 && mm - month > 0)
        return true;
    else if(yy - year == 18 &&mm - month == 0&&dd-day >= 0)
        return true;///else if()是指不满足yy-year>18之外满足()的情况,所以一定要加上yy-year==18这个前提条件
    else//除上述三种情况之外都不成年
        return false;
}
int main()
{
    scanf("%d.%d.%d %d.%d.%d",&dd,&mm,&yy,&bd,&bm,&by);
    //枚举六种搭配进行check判断
    if(check(by,bm,bd)== true)cout<<"YES";
    else if(check(by,bd,bm) == true)cout<<"YES";
    else if(check(bm,by,bd) == true)cout<<"YES";
    else if(check(bm,bd,by) == true)cout<<"YES";
    else if(check(bd,by,bm) == true)cout<<"YES";
    else if(check(bd,bm,by) == true)cout<<"YES";
    else cout<<"NO";
    return 0;
}

下午,开始刷题(第二周题组)

题目如下:

题目描述

给出一棵二叉树的中序与后序排列。求出它的先序排列。(约定树结点用不同的大写字母表示,长度\le 8≤8)。

输入格式

22行,均为大写字母组成的字符串,表示一棵二叉树的中序与后序排列。

输出格式

11行,表示一棵二叉树的先序。

输入输出样例

输入 #1复制

BADC
BDCA

输出 #1复制

ABCD

思路:

1.输入的是一个二叉树的中序序列和后序序列

2.由后序序列的特点可知,后序序列末尾结点为根结点。

3.在中序序列中找到该结点,它的两边是根结点的两棵子树,而他们所对应的后序序列在总后序序列中可以找到,最后一个结点为子树的根。

4.题目要求的是先序序列,所以我们要不断寻找根,根的子树的根,输出根,即得到先序学列,寻找方法都是一样的所以我们想到用递归。 

由于在写代码的过程遇到了困难,感觉不断地提子字符串很麻烦,所以又学到了一手函数,再次感叹c++就是比c牛掰qwq!!

SUBSTR ()函数(子串截取函数) 

substr函数格式   (俗称:字符截取函数)

  格式1: substr(string string, int a, int b);

  格式2:substr(string string, int a) ;

解析:

    格式1:
        1、string 需要截取的字符串
        2、a 截取字符串的开始位置(注:当a等于0或1时,都是从第一位开始截取)
        3、b 要截取的字符串的长度

    格式2:
        1、string 需要截取的字符串
        2、a 可以理解为从第a个字符开始截取后面所有的字符串。

挣扎了很久之后发现学了这个函数我也搞不出来这道题,感觉自己的代码好像没什么毛病了可是就是一直报错.......大佬的代码是这样子的,完全正确,高级又简单:

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
void beford(string in,string after)
{
    if (sizeof(in)>0)
    {
        char ch=after[after.size()-1];
        cout<<ch;//找根输出
        int k=in.find(ch);
        beford(in.substr(0,k),after.substr(0,k));
        beford(in.substr(k+1),after.substr(k,in.size()-k-1));//递归左右子树;
    }
}
int main()
{
    string inord,aftord;
    cin>>inord;
    cin>>aftord;//读入
    beford(inord,aftord);
    cout<<endl;
    return 0;
}

 然而我的..

#include<bits/stdc++.h>
using namespace std;
string intree,postree;
int finds(string ch,char poos,int l)
{
    for(int i=0;i<l;i++)
        if(ch[i] == poos)
        return i;
}
void pretree(string in,string pos,int l)
{
    if(l>0)
    {
        char root=pos[l-1];
        cout<<root;//输出根
        int k=finds(in,root,l);
        pretree(substr(in,0,k),substr(pos,0,k));
        pretree(substr(in,k+1,l-1),substr(k,l-k-1));

    }
}
int main()
{
    cin>>intree>>cin>>postree;
    int len=strlen(postree);
    pretree(intree,postree,len,len);
    return 0;
}

都运行不了.....!!我明明是按照格式使用的可是会出现这样子的报错..!!

放弃这个方法了,还是走dfs吧..........................................

如何由后序序列和中序序列推出前序序列,很明白!但就是捋不清代码怎么写。借鉴了别人的之后有了思路:

通过记录中序序列起止点下标和对应后序序列起止点下标来模拟提出子字符串!(这还真是个非常不错的好办法),通过手算模拟过程推出各个下标的规律,发现还需要知道右子树的结点个数会更好进行递归操作,那就在“遍历中序序列(注意这里是从后往前遍历//是为了求右子树结点个数)寻找根结点的过程记录一下右子树结点个数”,便于描述左子树和右子树的中序序列和后序序列的起末点下标。

自定义这样子的一个函数

void dfs(int start1,int end1,int start2,int end2)

找到当前根结点在中序序列中的位置下标i,并记录了右子树序列个数k之后,

下一步通过递归操作分别处理左右子树

通过找规律得到

左子树中序序列起点:start1

左子树中序序列终点:i-1

左子树后序序列起点:start2

左子树后序序列终点:end2-k-1

右子树中序序列起点:i+1

右子树中序序列终点:end1

右子树后序序列起点:end2-k

右子树后序序列终点:end2-1

结束dfs的条件是当起点大于终点时,即:

if(start1>end1||start2>end2)//如果起点下标大于终点,回溯
        return;

#include<bits/stdc++.h>
using namespace std;
char s1[10],s2[10];
void dfs(int start1,int end1,int start2,int end2)//start1:中序序列起点下标,end1:中序序列终点下标,start2:后序序列起点下标,end2:后序序列终点下标
{
    if(start1>end1||start2>end2)//如果起点下标大于终点,回溯
        return;

    char ch=s2[end2];//输出后序序列末尾元素
    cout<<ch;

    for(int i=end1,k=0;i>=start1;k++,i--)///从末尾往前遍历,所以其中k记录当前根的右子树元素个数
    {
        if(s1[i]==ch)
        {
            dfs(start1,i-1,start2,end2-k-1);//递归处理左子树
            dfs(i+1,end1,end2-k,end2-1);//递归出库右子树
          ///右子树后序序列下标范围是end2-k到end2-1
        }
    }
}
int main()
{
    cin>>s1>>s2;
    int len=strlen(s1);
    dfs(0,len-1,0,len-1);//中序与后序序列的起末点下标
    return 0;
}

感谢乐于助人的同行小伙伴,我终于弄懂了这题,这道题的理解对我后续的题目帮助肯定会很大的。

接着做了并查集的题目,感谢某非常非常非常好的学长讲的超级棒,让我理解的很透彻啦。

题目如下

题目背景

若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。

题目描述

规定:xx 和 yy 是亲戚,yy 和 zz 是亲戚,那么 xx 和 zz 也是亲戚。如果 xx,yy 是亲戚,那么 xx 的亲戚都是 yy 的亲戚,yy 的亲戚也都是 xx 的亲戚。

输入格式

第一行:三个整数 n,m,pn,m,p,(n,m,p \le 5000n,m,p≤5000),分别表示有 nn 个人,mm 个亲戚关系,询问 pp 对亲戚关系。

以下 mm 行:每行两个数 M_iMi​,M_jMj​,1 \le M_i,~M_j\le N1≤Mi​, Mj​≤N,表示 M_iMi​ 和 M_jMj​ 具有亲戚关系。

接下来 pp 行:每行两个数 P_i,P_jPi​,Pj​,询问 P_iPi​ 和 P_jPj​ 是否具有亲戚关系。

输出格式

pp 行,每行一个 Yes 或 No。表示第 ii 个询问的答案为“具有”或“不具有”亲戚关系。

输入输出样例

输入 #1复制

6 5 3
1 2
1 5
3 4
5 2
1 3
1 4
2 3
5 6

输出 #1复制

Yes
Yes
No

代码如下

#include<bits/stdc++.h>
using namespace std;
int n,m,q;
int f[10000];
int findd(int x)//找祖宗
{
    if(f[x]==x)//自己就是自己的爸爸
        return x;
    else
        return f[x]=findd(f[x]);//顺便把内部结点的爸爸直接改成老大
}
void combine(int x,int y)
{
    if(findd(x)!=findd(y))
        f[findd(x)]=findd(y);
}
int main()
{
    cin>>n>>m>>q;
    for(int i=1;i<=n;i++)//初始化f数组
        f[i]=i;

    for(int i=0;i<m;i++)
    {
        int a,b;
        cin>>a>>b;
        combine(a,b);
    }
    for(int i=0;i<q;i++)
    {
        int a,b;
        cin>>a>>b;
        if(findd(a) == findd(b))
            cout<<"Yes"<<endl;
        else
            cout<<"No"<<endl;
    }
    return 0;
}

 当时看并查集的时候就浏览了一遍,下午讲课讲了,晚上学长又讲的很清楚,所以写一波自己的认识和思路吧。

┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄*

并查集,通俗的讲就是把一伙的归到一起去。/把一家的人放到同一棵树上去。

就这道题而言,就是由一些基本操作组成的:初始化父亲数组,查祖宗,合并小集合,判断。

初始化:起初用f[x]=x,使得每个人是自己的爸爸///有点奇怪 应该说就是自己就是自己的老大!

查祖宗:如果f【x】=x,那就是找到祖宗了,否则,通过递归一直往上找直到找到f【x】=x的点,两个人的祖宗一样那他们肯定就是一家的嘛!这里可以有一个压缩路径的操作,即f[x]=findd(f[x]),这样子即把所有内部结点都直接指向根结点了,这样子查找祖宗的时候就可以一步到位!节省时间,呸,可以减小时间复杂度!

合并集合(不能漏了噢):由于给出的信息,一个人可能同时和几个人有关系嘛,不能丢三落四,如果输入一对有亲戚关系的人a和b,仅仅令f[a]=b的话,那么如果有另一个信息是a和c有亲戚关系的话,变成了f[a]=c,a和b的关系就断开了。所以我们要用这么一个合并操作避免这样的事情发生,即,给出两个有亲戚关系的人a,b,如果他们的祖先不一样,就让他们两其中一个人的祖先认另一个人为祖先,这样子就把他们联系起来了,有了合并操作,就可以不需要f[a]=b这个操作了,因为当a和b自己是自己的祖先时候,"他们两其中一个人的祖先认另一个人为祖先"就等同于f[a]=b,想明白这个就可以理解为什么可以输入数据时,直接进行合并操作了。ok并查集确实简单。

┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┅┄┄*

今天就先到这里,毕竟凌晨就是第二天了qwq

有一说一,c++一用起来,就停不下来了,不想回去了

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值