码蹄集2023部分题目(第二弹)

博客分享了四道算法题,包括旅行、小码哥玩游戏、自动浇花机和手机测试。涉及并查集、矩阵、模拟等知识,详细阐述了各题的题目描述、输入输出格式、样例,重点给出了题目思路,还提及代码实现,为算法学习提供参考。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

5🐋🐋🐋MC0212旅行(钻石;并查集)

时间限制:1秒

占用内存:256M

🐟题目描述

假期快要结束了,小码哥还没有好好放松一下,便决定四处转转。小码哥所在的地方有 N 个景点和 M 条有向道路连接这些景点。景点从 1 到 N 编号,道路从 1 到 M 编号,第 i 条道路连接景点 UiUi 和 ViVi,这意味着小码哥可以从景点 UiUi 走到景点 ViVi。

小码哥想到每个景点都看一看,他可以随意选择一个景点作为起点,并随意选择另一个景点作为终点。为了更好的欣赏沿途风景,小码哥决定制定一个计划,使他能够恰好只到每个景点一次并恰好只经过每条道路一次。现在给你所有景点和道路的信息。请告诉小码哥是否存在一种方法可以满足他的计划。

如果只有一个景点,则小码哥可以直接去这个景点,我们认为这种情况也是满足小码哥的计划的。

🐟输入输出格式

输入格式:
第一行包含一个整数 T,表示测试用例的数量;
对于每个测试用例,第一行包含两个整数 N 和 M,其中 N 是景点的数量,M 是道路的数量;
接下来 M 行,第 i 行包含两个整数 UiUi• 和 ViVi•,表示从景点 UiUi• 有一条指向景点 ViVi• 的有向道路。
​
输出格式:
输出包括T行,每i行表示第i个测试用例是否通过,通过输出YES,否则输出NO。

🐟样例

🐚样例1

输入:
2
4 3
1 2
2 3
3 4
4 2
1 2
3 4
​
输出:
YES
NO

🐚样例2

输入:
1
1 0
​
输出:
YES

🐚备注

其中:1≤T,N≤10^5,0≤M≤10^5
所有测试用例中的 N 之和不超过 10^6,M 之和不超过 10^6。

🐟题目思路

该题目用到的知识有:

  • 数据结构:并查集

  • 图论:图论基础

其实我一开始是没想到并查集的,第一反应是DFS深度优先遍历,但是我不会😂,然后看到了提示里的并查集。将1能到达的所有点的父子树信息记录到f数组中,将每个点能有几条路到达记录到indegree数组中,然后最后判断是否满足:是否所有点都可以从1到达;是否只有1不是到达点,也就是没有点到1(回来成环);是否只有一条路可到达。

🐟代码

#include<bits/stdc++.h> 
using namespace std;
const int N=10e5+10,M=1e5+10;
int n,m;
int f[N];//f数组,用来存储相连集合
int indegree[N];//这个数组用来记录有多少条道路能到该点(v)
int find(int x) //find函数,用来找x(v)的父节点(u)
{
    return f[x]==x?x:f[x]=find(f[x]);//不等于x意味着有父节点的相关记录了,找父节点返回
}
void merge(int a,int b)//merge函数,用来将b点连向a点,表示a点可以到达的集合中包含b;同时进行了树高的压缩,让b直接连到a的最祖的父节点上了
{
    f[find(a)]=find(b);
}
​
int main( )
{
    int T;
    cin>>T;//T轮
    for(int k=0;k<T;k++)
    {
        cin>>n>>m;
        //将f、indegree数组清空
        memset(f,0,sizeof f);
        memset(indegree,0,sizeof indegree);
        for(int i=0;i<n;i++) f[i]=i;//初始化f数组
        for(int i=0;i<m;i++)//输入u、v信息并合并集合的信息
        {
            int u,v;
            cin>>u>>v;
            merge(u,v);
            indegree[v]++;//能到v点的道路数加一
        }
        int count0=0,count1=0;
        int flag=1;//用来标记是否有某一点无法从1到达
        for(int i=1;i<=n;i++)
        {
            if(indegree[i]==0) count0++;//表示没有点到我这里的道路(应该是1专属的)
            else if(indegree[i]==1) count1++;//表示只有一条道路到我这里
            if(find(1)!=find(i)) flag=0;//意味着从1无法到达我这里
        }
        //如果从1可以到所有点;并且只有1是发出点不是到达点;并且其他所有点都是只有一条道路可到达
        if(flag==1&&(count1==n-1&&count0==1)) cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
    }
    return 0;
}

6🐋🐋🐋MC0215小码哥玩游戏(白银;矩阵;上下左右周围;模拟)

时间限制:1秒

占用内存:256M

🐟题目描述

小码哥和朋友在玩游戏,游戏内容为在给定的的m∗nm∗n列的数组中(1≤m,n≤1001≤m,n≤100),该数组中元素由[A, Z]和0组成,其中0表示该位置无字符,然后由玩家指定一个字符(字符范围为[A, Z]),最后统计与该字符相邻的字符数量,最终数量最多的人胜出。现在请通过编程帮助小码哥计算出给定一个字符后最终相邻的字符数。

请注意: • 最终的结果里,需要去重,比如,玩家指定的字符是A,A在[0,1]、[1,0]、[2,1]、[1,2]四个位置都有,而[1,1]是字符B,则B和这四个位置的字符A都是相邻的,但是B在最终的结果里只需要计数一次。 • 相邻只有上下左右相邻; • 相邻的字符如果和指定字符一致,则不统计该相邻字符,即对于数组BAA,如果给定字符为A,则相邻字符数为1,其中B计数,而右侧的A不计数。

🐟输入输出格式

输入格式:
第一行输入三个数据,且以空格分隔,前两个数为数组的行m和列n (其中1≤m,n≤1001≤m,n≤100),第三个字符为指定的字符;
后面n行m列表示这个数组的字符分布情况,所有字符都在[A, Z]之间,且两个字符之间没有空格。
​
输出格式:
打印唯一的数字,表示相邻的字符数量。

🐟样例

输入:
2 3 H
00A
0HH
​
输出:
1

🐟题目思路

这道题目很简单,直接矩阵存储,然后遍历暴力求解即可,这里主要是记录一下需要注意的点:

  • 与给定字符相同的字符不算在内

  • 最后给到的是所有给定的字符的周围的不同字符的个数

  • ⭐map中可能存在有的项的key为空的情况,这导致了我5个点的报错,所以在最后确定个数的时候要判断key是否为空

🐟代码

#include<bits/stdc++.h> 
​
using namespace std;
​
int main( )
{
    int n,m;
    char pointed;
    cin>>n>>m>>pointed;
    char a[110][110];
    string cur;
    for(int i=0;i<n;i++)
    {
        cin>>cur;
        for(int j=0;j<m;j++) a[i][j]=cur[j];
    }
    map<char,int> p;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            if(a[i][j]==pointed)
            {
                if(j-1>=0)
                {
                    if(a[i][j-1]!='0'&&a[i][j-1]!=pointed)
                    {
                        auto it=p.find(a[i][j-1]);
                        if(it==p.end()) p.insert({a[i][j-1],1});
                    }
                }
                if(i-1>=0)
                {
                    if(a[i-1][j]!='0'&&a[i-1][j]!=pointed)
                    {
                        auto it=p.find(a[i-1][j]);
                        if(it==p.end()) p.insert({a[i-1][j],1});
                    }
                }
                if(j+1<=m)
                {
                    if(a[i][j+1]!='0'&&a[i][j+1]!=pointed)
                    {
                        auto it=p.find(a[i][j+1]);
                        if(it==p.end()) p.insert({a[i][j+1],1});
                    }
                }
                if(i+1<=n)
                {
                    if(a[i+1][j]!='0'&&a[i+1][j]!=pointed)
                    {
                        auto it=p.find(a[i+1][j]);
                        if(it==p.end()) p.insert({a[i+1][j],1});
                    }
                }
            }
        }
    }
    int count=0;
    for(auto it=p.begin();it!=p.end();it++) 
    {
        if(it->first) count++;//这里需要注意
        //cout<<it->first<<endl;//
    }
    cout<<count<<endl;
    return 0;
}

7🐋🐋🐋MC0217自动浇花机(黄金;模拟)

时间限制:1秒

占用内存:256M

🐟题目描述

小码哥在花园里种植了一排花,一共nn种。为了浇花方便,他发明了一个自动的浇花器,一共有两个喷头,左边喷头从左往右浇花,右边的喷头从右往左浇花。左边的喷头的浇花的速度是右侧的两倍(同一个花,左边的喷头花费的时间是右边的一半)。 假设两个喷头同时开始浇花,左边的从左侧开始,右边的从右侧开始,浇完一朵马上浇下一朵。 特别提示: (1)如果左边的喷头到达一朵花的时候,右边的喷头正好在喷,则左边的喷头不会去喷这朵花,左边的喷头什么也不会干,因为这个时候已经没有剩余的花了。 (2)如果右边的喷头到达一朵花的时候,左边的喷头正好在喷,则右边的喷头不会去喷这朵花,右边的喷头什么也不会干,因为这个时候已经没有剩余的花了。 (3)如果左边和右边的喷头同时到达同一朵花,则由左边的喷头喷这朵花,右边的喷头什么也不会干,因为这个时候已经没有剩余的花了。 小码哥想知道左侧的喷头最终浇了多少多花。

🐟输入输出格式

输入格式:
第一行包含一个整数 n(1≤n≤1000)n(1≤n≤1000) 表示花的数量。
第二行包含序列t1,t2,…,tn(1≤ti≤1000),其中ti是右边的喷头这一朵花需要的时间。
​
输出格式:
打印左边的喷头浇花的数量。

🐟样例

输入:
7
10 5 3 1 5 2 1
​
输出:
3

🐟题目思路

这道题目是要求我们模拟题目过程,那么关键就在于怎么判断制胜点(左右喷头出现3种分歧的位置)到底在哪里。我想到可以用两个数组记录左右喷头单独工作到某个位置完成工作的准确用时。这样制胜点就出现在r[i]==l[i]和r[i]<l[i]&&r[i+1]>r[i+1]这两种情况的i位置。找到制胜点后,就要看制胜点两边的数的具体大小,也就是l[i-1]和r[i+1]的具体大小,如果相等,说明二者同时到i这个位置,让左喷;如果l小,说明左喷头先到,让左喷;如果r小,说明右喷头先到,让右喷。

除此之外,我卡在了8和13这两个数据点,绞尽脑汁,发现是开头和结尾有两种极端情况:右已经喷完剩下的n-1个了,左还在喷或者刚喷完第1个;左已经喷完剩下的n-1个了,右还在喷或刚喷完第n个。第一种情况要注意,左也是喷了1个的,所以输出1;第二种情况,右也是喷了1个的,所以输出n-1。

🐟代码

#include<bits/stdc++.h> 
​
using namespace std;
​
int main( )
{
    int n;
    cin>>n;
    int a[1010]={0};
    for(int i=0;i<n;i++) cin>>a[i];
    double l[1010]={0},r[1010]={0};
    l[0]=a[0]/2.0;
    r[n-1]=a[n-1];
    for(int i=1;i<n;i++) l[i]+=l[i-1]+a[i]/2.0;
    for(int i=n-2;i>=0;i--) r[i]+=r[i+1]+a[i];
   // for(int i=0;i<n;i++) cout<<l[i]<<" "<<r[i]<<endl;//
   if(l[0]>=r[1]) 
   {
        cout<<1<<endl;
        return 0;
    }
    if(l[n-2]<=r[n-1])//8和13的制胜点
    {
        cout<<n-1<<endl;
        return 0;
    }
    for(int i=0;i<n;i++)
    {
        if(l[i]==r[i]) 
        {
            cout<<i<<endl;
            break;
        }
        else if(l[i]<r[i]&&l[i+1]>r[i+1])
        {
            if(l[i-1]<=r[i+1]) cout<<i+1<<endl;
            else cout<<i<<endl;
            break;
        }
    }
    return 0;
}

8🐋🐋🐋MC0224手机测试(白银;模拟)

时间限制:1秒

占用内存:256M

🐟题目描述

小码哥最近想测试下手机的性能,当前该手机有三种模式:省电模式、正常模式和性能模式。当使用手机玩游戏的时候,手机会自动进入性能模式,性能模式每分钟消耗电量为m,当退出游戏后,手机会在经过t1分钟后进入正常模式,正常模式下每分钟消耗电量为n。然后正常模式运行t2 时间后,手机会在后台自动关闭游戏进程,从而进入到省电模式,省电模式下手机每分钟消耗电量为 k。如果小码哥在手机处于正常模式或省电模式的情况下可直接开启游戏从而使手机进入到性能模式。假如小码哥将会在以下 aa个时间段 [l1,r1],[l2,r2],…,[la,ra] 玩游戏,请帮忙计算下从 l1 到 ra 这个时间段使用的总电量。 注意:每个时间段不重合,即 ra−1<la。

🐟输入输出格式

输入格式:
一行需要输入6个整数:a,m,n,k,t1,t2,(1≤a≤100,0≤m,n,k≤100,1≤t1,t2≤100);
以下 a行包含小码哥玩游戏的时间段。 其中每行存在包含两个整数(使用空格分隔) l 和 r(0≤l<r≤5000),分别代表该时间段玩游戏的开始和结束,且下一段开始的时间比上一段的结束时间要大。
​
输出格式:
输出小码哥的手机在 [l1,ra] 期间消耗的总电量。

🐟样例

🐚样例1

输入:
2 4 5 6 5 8
30 40
60 100
​
输出:
302

🐚样例2

输入:
3 40 22 31 11 15
40 52
60 90
210 250
​
输出:
7284

🐟题目思路

这道题目的思路比较简单,就是模拟模式转换过程,那么重点就在于明确模式是如何转换的:

游戏,性能模式,消耗m→(经过t1进入正常模式,消耗m)→(经过t2进入省电模式,消耗n)→省电模式,消耗k

那么游戏的性能模式部分就是输入长度为多少就是多少乘以m;转换时先判断是否足以进入正常模式,进而判断是否足以进入省电模式。

具体我的代码中有注释,可以看一下。

🐟代码

#include<bits/stdc++.h> 
​
using namespace std;
​
int main( )
{
    int a,m,n,k,t1,t2;//性能模式m,t1进入正常模式,正常模式n,t2进入省电模式,省电模式k
    cin>>a>>m>>n>>k>>t1>>t2;
    vector<vector<int> > ans(a,vector<int>(2,0));
    int res=0;
    for(int i=0;i<a;i++)
    {
        cin>>ans[i][0]>>ans[i][1];
        res+=(ans[i][1]-ans[i][0])*m;//计算游戏时性能模式的耗电量
    }
    //计算空闲时的耗电量
    for(int i=1;i<a;i++)
    {
        int cost=ans[i][0]-ans[i-1][1];//计算空闲时长
        //空闲时先进入正常模式,这段时间内还是消耗性能模式的电量
        if(cost>=t1)
        {
            res+=t1*m;
            cost-=t1;
        }
        else
        {
            res+=cost*m;
            continue;//这个时间无法进入正常模式,全部使用性能模式的耗电量
        }
        //如果还剩余空闲时间,那么接下来进入省电模式
        if(cost>=t2)
        {
            res+=t2*n;
            cost-=t2;
        }
        else
        {
            res+=cost*n;
            continue;//剩下的空闲时间不足以进入省电模式,全部是正常模式的耗电量
        }
        if(cost>0) res+=cost*k;//如果还有空闲时间,那么全部是省电模式的耗电量
    }
    cout<<res<<endl;
    return 0;
}

⭐点赞收藏不迷路~

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值