码蹄集2023题目(第三弹)

今天这一期是来还债的,都是之前跳过的几个比较难的题目,大多是思路上走不太通或者走得通但落实不出来的,感谢提供思路的朋友~

有难度的一期,废话不多说,先上题目为敬~

1🐋🐋🐋MC0219自驾游(钻石;动态规划)

时间限制:10秒

占用内存:256M

🐟题目描述

小码哥最近刚考完驾照,又恰逢暑假将到,便决定自驾游一番,一来可以欣赏大美新疆,二来还能磨炼驾驶技术。

小码哥查阅各种旅游资料后,挑选了几个旅游胜地,以及几条风景不错的公路,并规划出总行程 L 的路线。由于新疆太大了,小码哥必须要在沿途多次加油才能跑完全程,在他规划的这条路线上共计 N个加油站,可以直接加满。第 ii 个加油站位于 Pi,想要在这里加满需要花费 Ci块钱,一个位置上可能有多个加油站。

小码哥的车油量上限为 Maxn,初始的时候视为油量满额,每消耗一单位油,可以跑一单位行程。当油量见底的时候,车就会抛锚(如果当前位置有加油站,他仍然可以加油)。小码哥预算可拿出 S 块钱用于加油,他想知道自己能否完成这次自驾游。

🐟输入输出格式

输入格式:
本题包含多组数据,每组数据包含N+1N+1行:
第一行输入四个正整数N,L,Maxn,S;
接下来NN行,每行输入两个正整数Pi,Ci,其中Pi不一定是顺序给出的。
​
输出格式:
每组数据输出一行,如果小码哥能够完成自驾游,那么输出”Yes”,否则输出”No”。

🐟样例

🐚样例1

输入:
1 1000 500 2000
500 2000
1 1000 500 2000
500 3000
​
输出:
Yes
No

🐚备注

其中:0≤N≤2000, 0≤L≤20000, 0≤Maxn≤20000, 0≤S≤2000;1≤Pi≤N, 1≤Ci≤3000

🐟题目思路

这道题,官方提示是BFS(广度优先遍历),但是我不太会,所以学习的评论区的思路,使用动态规划。

🐟代码

#include<bits/stdc++.h> 
​
using namespace std;
​
bool cmp(pair<int,int> a,pair<int,int> b)//自定义的比较大小的函数,用于sort中规定排序方式
{
    return a.first<b.first;
}
​
int main( )
{
    int N,L,MAX,S,flag=0;
    while(cin>>N>>L>>MAX>>S)//※用于判断后方是否还有字符
    {
        //flag=0;
        map<int,int> m;
        m[0]=0;//0是出发点
        int a,b;//(优化思想)这是一个优化点,如果定义放在for循环中,那么会申请N次内存,这样只用申请1次
        for(int i=0;i<N;i++)
        {
            cin>>a>>b;
            m[a]=b;//map可以直接赋值,位于a的加油站需要花b元
        } 
        
        //加油站信息放到数组里排序,模拟真实加油的情况
        vector<pair<int,int> > data(m.begin(),m.end());
        sort(data.begin(),data.end(),cmp);//按加油站位置进行排序,这就是模拟路上到加油站的情况
        
        int dp[2010]={0};//动态规划数组,记录到当前位置最少需要花多少钱
        for(int i=1;i<=N;i++)//0是出发点,所以从第1个加油站开始判断
        {
            dp[i]=INT_MAX;//※用于直接设置为无穷大
            for(int j=0;j<i;j++)//遍历当前所在加油站前方的所有加油站,用于接下来的判断
            {
                //1:判断能否到达
                if(data[j].first+MAX<data[i].first) continue;//前边的任意一个加油站都不足以让小码哥到当前加油站所在位置,排除这个加油站
                //第j个加油站可以让小码哥到当前位置的加油站
                //2:判断还有没有钱
                dp[i]=min(dp[i],dp[j]+data[j].second);//※※※动态规划更新函数※※※
                //如果不加油,到当前位置的花费就是dp[i],加油就是后边那项
                if(dp[i]+data[i].second<=S&&data[i].first+MAX>=L) //如果此时,花的钱少于等于小码哥有的钱,并且剩下的油足以到达重点了,那么就可以输出yes结束了
                {
                    cout<<"Yes"<<endl;
                    //flag=1;
                    //break;
                    goto flag;
                }
            }
            //if(flag==1) break;
        }
        //if(flag==0) cout<<"No"<<endl;
        cout<<"No"<<endl; //所有情况都不能输出yes,说明没法满足,输出no
​
    flag: //用于跳转的空语句,goto flag后会直接到当前位置,跳过NO的输出
        ;
    }
    return 0;
}

代码思路来自题目评论区——小码_112662

  • 我刚开始用flag做标记来判断是否输出no和何时结束,但是第3个数据点一直过不了,有没有懂的大佬们帮忙看看解答一下~ 谢谢🌹~

  • 还有一个地方就是goto flag那里,我对goto语句比较陌生,有时候flag: ;可以执行过第3个数据点,有时候过不了,有没有懂的大佬们帮忙解答下~ 谢谢🌹~

2🐋🐋🐋MC0221未来战争(黄金)

时间限制:4秒

占用内存:256M

🐟题目描述

公元 2099 年,小码哥和小码弟所在的两个国家陷入战争。小码哥所在的城市需要用激光武器来防守,而激光武器需要定时充能。小码哥和他的战友们每天都要凌晨 4 点起床,然后去为守城的激光武器充能。

我们可以把每个人为激光武器充能的时间段看作一段区间 [l, r](以秒为单位,从凌晨 4 点开始计时),其中 l 表示充能的开始时刻,r 表示充能的结束时刻。例如,如果一个人在 300 秒的时候开始充能,直到 1000 秒的时候结束,我们可以表示为区间 [300, 1000]。

现在,我们有一个列表,其中包含了 n 个人为 n 个激光武器充能的时间段,我们想要计算以下两个问题: 1.最长的至少有一个人在为激光武器充能的连续时间段是多长? 2.最长的没有人为激光武器充能的连续时间段是多长?

假设有 3 个人,他们的充能时间段分别为 [300, 1000],[700, 1200] 和 [1500, 2100]。则最长的至少有一个人在为激光武器充能的连续时间段为 900 秒(从 300 秒到 1200 秒),最长的没有人为激光武器充能的连续时间段为 300 秒(从 1200 秒到 1500 秒)。

🐟输入输出格式

输入格式:
输入第一行为一个正整数 n;
接下来输入 n 行,每行两个非负整数 l,r,表示一个人充能的开始时刻与结束时刻。整数之间以空格间隔。
​
输出格式:
输出一行,两个整数,表示最长至少有一人在充能的时间段和最长的无人充能的时间段。整数之间以空格间隔。
如果从第一个人开始充能,到最后一个人充能结束,都不存在无人充能的时间段,那么对于第二个问题应该输出0。

🐟样例

🐚样例1

输入:
3
300 1000
700 1200
1500 2100
​
输出:
900 300

🐚备注

其中:1≤n≤5×10^3,0≤l≤r≤10^6。
关于连续区间的判断:若充能区间为[1,2],[2,6],则在[1,6]时间区间内都视为在充能;若充能区间为[1,2],[3,5],则在[2,3]区间内无人充能。 

🐟题目思路

这道题目官网给的思路是前缀和,但是我不太会,所有参考了评论区的思路,类似双指针,也类似模拟。具体步骤就是:

  • 两个二维数组a和b,分布记录所有区间信息和每个最长的连续区间段

  • 数组a进行排序便于顺序遍历所有的区间左右端点

  • 数组b的更新方式:如果当前区间的左端点还在b的最后一个区间范围内,那么就直接更新b中最后一个区间的右端点为二者的最大值即可;否则,b中插入新区间

  • ans1记录最长连续充能区间,就是最长的b中区间

  • ans2记录最长间断区间,那就是两个b中区间的最长(后一个区间的)前端点减(前一个区间的)后端点

🐟代码

#include<bits/stdc++.h> 
​
using namespace std;
​
int main( )
{
    int n;
    cin>>n;
    int l,r;
    vector<vector<int> > a,b;//※动态二维数组;a用来记录区间数据,b用来记录每个(最长化处理后的)连续区间
    for(int i=0;i<n;i++)//插入区间
    {
       cin>>l>>r;
       a.push_back({l,r}); //※第i个位置的数组将被插入两个数
    }
    sort(a.begin(),a.end());//排序,为了顺序遍历所有区间的左右端点
    
    int ans1=0,ans2=0,ll,rr;//ans1是最长的充能区间,ans2是最长的非充能时间
    for(int i=0;i<a.size();i++)//遍历所有区间
    {
        //得到每个区间的左、右时间
        ll=a[i][0];
        rr=a[i][1];
        if(!b.size()||b.back()[1]<ll) b.push_back({ll,rr});//如果数组b为空或者b的最后一个区间的右端点比当前区间的左端点小:插入新区间
        else b.back()[1]=max(b.back()[1],rr);//其他情况(开始的区间还在当前区间内):b的最后一个区间的右端更新为最远的那个
        ans1=max(ans1,b.back()[1]-b.back()[0]);
    }
    for(int i=1;i<b.size();i++) ans2=max(ans2,b[i][0]-b[i-1][1]);
    cout<<ans1<<" "<<ans2<<endl;
    return 0;
}

代码思路来自题目评论区——DTS小花

欢迎大家提供更多样、高效的代码,如果后续知道上述的问题的原因了,我也会在评论区更新的,我们评论区见~

⭐点赞收藏不迷路~

  • 14
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值