废柴日记2:平日里我大大咧咧,周赛时我戛然而止。

前言

博主真的很想经常发那种理论知识讲解的博客,但奈何博主材朽行秽,只是一介莽夫,遇到问题总是想着暴力莽一发再说,所以像

废柴日记1:Python与C++中关于随机数的那些事_Mr.RainsdRop的博客-CSDN博客前言之前看到过一篇博客,讲的是如何使用Java中的类写一个“女朋友”出来。看完整篇博客之后自己的第一反应肯定是我上我也行。不过如果是当时的自己,是肯定不敢写成博客发出来供其他人观看的。现在想想,自己一开始写博客的初衷就是想用幽默有趣的语言搞定枯燥无趣的知识,并希望可以帮到其他人。为什么这么简单的事自己却退缩了?虽然不是很明白,但是咸鱼了很久的博主也是准备翻身了。正文================================================= 废..https://blog.csdn.net/qq_45750296/article/details/120231219

这种质量的日记暂时只能维持一个周更的情况。

但「废柴日记」系列不会断更,平日里会以一些有趣的题目作为日记内容。博主争取每周出一篇知识讲解主题的保质保量的「废柴日记」。


=================================================

          废柴日记2:平日里我大大咧咧,周赛时我哑然无声。        

=================================================

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

日期:2021年9月12日                                                     天气:晴

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

其实,我每天都还好。

开学一周之后的周赛,就像是高中生每次开学之后的摸底考试,令人提心吊胆、后悔莫及、悔不当初、抱恨终生、含恨而终......

虽然博主内心十分的懊悔与痛苦,甚至巴不得比完赛就当场上票。

但是,博主十分迷信。我相信这个世界的善恶之比一直维持着守恒的局面,每当一个「恶人」出现时,必将出现一个「善人」来维持平衡的局面。

转念一想,像博主这样的「恶人」如果消失了,那就有可能会有一个「善人」消失。

博主只是一个拉低群体智力的「恶人」,心中还是存有一丝善念的。于是博主满血复活,开(bu)心(shi)地来写日记了。


周赛其一:牛战士从不摘下他的面具

自私的放牛——Selfish Grazing牛客网是互联网求职神器,C++、Java、前端、产品、运营技能学习/备考/求职题库,在线进行百度阿里腾讯网易等互联网名企笔试面试模拟考试练习,和牛人一起讨论经典试题,全面提升你的技术能力https://ac.nowcoder.com/acm/problem/24867

题目大意

农夫约翰的家里养了N头牛头人[doge],每只牛头人都有自己的领域范围【Si , Ei】。

大多数农民都认为:牛头人们十分自私,它们不准其他牛头人踏入自己的领地。但事实上,如果牛头人A的领域范围为【SA , EA】,牛头人B的领域范围为【SB , EB】,只要满足条件之一:

  1. SA >= EB
  2. EA <= SB

牛头人们还是可以和谐共处的。

现在约翰家有一块自由领域,请问他最多能安置多少头牛头人在这片领域中过着安静的生活呢?

博主の脑回路

"woc,好像不是很会啊......"

"等一下,有点眼熟,涂色问题?不会是线段树吧?"

"先排序吧,按照S的值从小到大排,S相同的话...按E的值从小到大排。"

首先解释为什么一号优先级是S:假设我们并不知道每个牛头人E的值,只知道它们S的值,为了安排更多的牛头人,我们会按照S的值,由小到大的顺序给它们安排住所。这里可以联想我们往书包中放东西的时候,总是先往包的底部放东西,无论多大。

其次是二号优先级:如果我们去超市买东西,每一件物品都是免费的,只是大小不同,为了追求买的物品的数量尽可能地多,我们肯定选小一些的物品。同理,当S的值相同时,E越小,这个牛头人的占地面积越小,我们越想给他安排地方住。

const int maxn=1e5+1000;

struct node
{
    int s,e;
    node(){}
    node(int ss,int ee):s(ss),e(ee){}
}a[maxn];
 
bool cmp(node x,node y)
{
    if(x.s==y.s) return x.e<y.e;
    return x.s<y.s;
}

for(int i=1;i<=n;i++)
    cin>>a[i].s>>a[i].e;
sort(a+1,a+1+n,cmp);

想出这些之后,博主就陷入了长时间的死机,没想出来怎么处理排好序的数据。

不知道过了多久,我看着草稿纸上的样例:

(1 , 12)  (2 , 4)  (4 , 5)  (7 , 8)  (7 , 10)

突然脑子里金光一闪:"如果把括号去掉呢?"

1 , 12 , 2 , 4  , 4 , 5 , 7 , 8 , 7 , 10

看到这里,我顿时醒悟了。但还有一个点需要确认一下:S与E的大小关系。

"不说了,芜湖起飞~"

啊,这个时候一定会有人想了:"woc,博主疯了"。

不不不,你觉得我很lao,很菜。

你只看到了第一层,

把我想成了负一层,

实际上,我在臭氧层。

这个问题经过这么一番处理其实已经变成了求最长非递减子序列

看不懂红字的朋友可以先去学习一下LIS(最长上升子序列)
(有机会博主也会出一篇博客讲解一下。额,好像在挖坑.....)

我们换个角度去思考这个问题,题目需要我们求出有几个牛头人可以生活在一起,只要求输出数量,那我们好人做到底,我们去求哪几个牛头人可以组成这个生活圈。

假设我们把最终生活在一起的牛头人的领域范围由小到大排序输出,我们会发现:

这是一个不递减的偶数长度的序列。(博主词穷,只能用如此拙劣的词语形容)

  • 严格递增:1 2 3 4 5 6
  • 严格递减:6 5 4 3 2 1
  • 不递减:1 1 1 1 2 3 3 3 
  • 不递增:3 3 3 3 3 2 1 1 1 1 0

这个序列中牛头人的数量就是 序列长度/2 。

我们来解释一下核心过程(依照上文中的样例):

(1 , 12)  (2 , 4)  (4 , 5)  (7 , 8)  (7 , 10)

首先,我们需要一个数组 dp 来记录当前的领域中有哪些牛头人,接下来我们把一号牛头人扔进领地中:

数组下标 1 2 3 4 5 6 7 8
元素 1 12

 接下来就轮到二号牛头人(2 , 4)了,此时我们看一下理想状态下满足条件可以进入领域的牛头人的S应该是多少?

数组下标 1 2 3 4 5 6 7 8
元素 1 12

 根据题目中给出的条件,此时的S应该至少为12。这样的话,我们的二号牛头人是无法进入领域的。此时需要运用到LIS的二分解决方法中的思想:

既然无法进入,那我们是否能让二号牛头人取代领域中的一头牛头人的位置,使得后面能够有更多的牛头人进入领域呢?

此时我们在 dp 数组中二分查找第一个大于等于4(E2)的位置pos。

pos = lower_bound(dp+1,dp+1+dp.size(),E2)-dp;

如果存在这个位置,我们找到的一定是领域中已经存在的一头牛头人的E值的位置。

数组下标 1 2 3 4 5 6 7 8
元素 1 12

此时pos==2,那么我们可以得知:这头牛头人的S为 dp[ pos - 1 ] ,E为 dp[ pos ]。

这接下来我们要看看能不能让二号牛头人顶替掉这个牛头人。

顶替的条件其实只有一条:

当原本的牛头人被新牛头人顶替时,在旧牛头人之前进入领域的牛头人仍然可以和新牛头人和谐相处。也就是当S1 <= S2 时,牛头人2才能顶替牛头人1。

此时我们将牛头人1替换为牛头人2:

数组下标 1 2 3 4 5 6 7 8
元素 2 4

我们再来看牛头人三号【4,5】,它可以直接进入领域之中:

数组下标 1 2 3 4 5 6 7 8
元素 2 4 4 5

剩下的「以此类推」。
(「以此类推」以前是我最恨的一个词,现在却经常用。有时候真的就是「以此类推」即可)

头脑风暴结束,看代码吧。

AC代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+1000;
struct node
{
    int s,e;
    node(){}
    node(int ss,int ee):s(ss),e(ee){}
}a[maxn];

bool cmp(node x,node y)
{
    if(x.s==y.s) return x.e<y.e;
    return x.s<y.s;
}

int b[maxn];
int dp[maxn];

int main()
{
    int n,ans=0;;
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i].s>>a[i].e;
    sort(a+1,a+1+n,cmp);
    for(int i=1;i<=n;i++)
        b[2*i-1]=a[i].s,b[2*i]=a[i].e;

    dp[1]=b[1];dp[2]=b[2];
    int pos=2;
    for(int i=3;i<=2*n;i+=2){
        if(b[i]>=dp[pos]){
            dp[++pos]=b[i];
            dp[++pos]=b[i+1];
        }
        else{
            int x=lower_bound(dp+1,dp+1+pos,b[i+1])-dp;
            if(b[i]>=dp[x-1]) dp[x]=b[i+1],dp[x-1]=b[i];
        }
    }
    cout<<pos/2<<endl;
}

周赛其二:后缀和是什么鬼?

分割数组——Array Splitting牛客网是互联网求职神器,C++、Java、前端、产品、运营技能学习/备考/求职题库,在线进行百度阿里腾讯网易等互联网名企笔试面试模拟考试练习,和牛人一起讨论经典试题,全面提升你的技术能力https://ac.nowcoder.com/acm/problem/113870

题目大意

给定一个有序,长度为N的数组,你的任务是把它分割成K个数组。

每个数组中都有一个最大的数字Max,和一个最小的数字Min,我们认为一个数组的破损度为Max - Min。

请你算一下,将原本的数组分割之后,得到的K个数组的破损度之和最小为多少?

博主の脑回路

"差分,绝对是差分!"

 然后我写了一个差分,扔在那里,就去想牛头人那道题了。

牛头人那题一出,思想解放,领域展开,我直接宣布:

从现在开始,这个训练室,就叫做________!

此时,队长llm跑过来,跟我说了一句:"F是后缀和。"

我直接当场懵B,后缀和是个什么东西?

果然还是靠自己才有用,首先我们先随便写下一组符合题意的数据:

1 3 3 5 6 17

这个数组的破损度为 17 - 1 == 16。

接下来我们给它劈一刀,从3  5之间来一刀:

【1 3 3】 【5 6 17】

此时我们再次计算破损度:(3 - 1)+(17 - 5) == 14  ==  (5 - 3)

我们算出这个数组的差分数组(括号中的数字):

1(2)3(0)3(2)5(1)6(11)17

此时我们会发现,(3 - 1) + (17 - 5) == (2) + (1 + 11) == 14 == 数组和 - 2。

所以结论就是

你在哪里砍一刀,分裂之后产生的破损度 == 原数组破损度减去砍的那个地方的差值。

头脑风暴结束,上代码。

AC代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+100;

int a[maxn];

int main()
{
    int n,k;
    cin>>n>>k;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    vector<int> arr;
    for(int i=2;i<=n;i++)
        arr.push_back(a[i]-a[i-1]);
    sort(arr.begin(),arr.end());
    long long sum=0;
    for(int i=0;i<arr.size()-k+1;i++)
    sum+=arr[i];
    cout<<sum<<endl;
}

写到这里的时候一只蟑螂从我的笔记本键盘区爬过,哎,我也不想骂了,已经麻了。

睡觉睡觉!!!


吾日三省吾身:日更否?刷题否?快乐否?
已更,已刷,快乐滴很!
吾心满意足。

Guess you like

Origin blog.csdn.net/qq_45750296/article/details/120254824