牛客网-数据结构笔试题目(一)-猫咪特征提取思路解析(附源码)

题意

小明是一名算法工程师,同时也是一名铲屎官。某天,他突发奇想,想从猫咪的视频里挖掘一些猫咪的运动信息。为了提取运动信息,他需要从视频的每一帧提取“猫咪特征”。一个猫咪特征是一个两维的vector<x, y>。如果x_1=x_2 and y_1=y_2,那么这俩是同一个特征。

因此,如果喵咪特征连续一致,可以认为喵咪在运动。也就是说,如果特征<a, b>在持续帧里出现,那么它将构成特征运动。比如,特征<a, b>在第2/3/4/7/8帧出现,那么该特征将形成两个特征运动2-3-4 和7-8。

现在,给定每一帧的特征,特征的数量可能不一样。小明期望能找到最长的特征运动。

输入描述:

第一行包含一个正整数N,代表测试用例的个数。

每个测试用例的第一行包含一个正整数M,代表视频的帧数。

接下来的M行,每行代表一帧。其中,第一个数字是该帧的特征个数,接下来的数字是在特征的取值;比如样例输入第三行里,2代表该帧有两个猫咪特征,<1,1>和<2,2>
所有用例的输入特征总数和<100000

N满足1≤N≤100000,M满足1≤M≤10000,一帧的特征个数满足 ≤ 10000。
特征取值均为非负整数。

输出描述:

对每一个测试用例,输出特征运动的长度作为一行
输入例子1:

1
8
2 1 1 2 2
2 1 1 1 4
2 1 1 2 2
2 2 2 1 4
0
0
1 1 1
1 1 1

输出例子1:

3
例子说明1:

特征<1,1>在连续的帧中连续出现3次,相比其他特征连续出现的次数大,所以输出3

题解

题目的题意还是比较清楚的,即找出最长连续出现的特征数量。

首先,对于题目当中的特征是用两个int的pair对代表的,相同的pair被视为是同样的特征。特征必须要连续出现才算,中间中断则重新计算。

比较容易想到,我们可以使用map来存储所有的特征以及它当前出现的最多次数。这样我们虽然搞定了存储问题,但还需要解决另外两个问题。第一个问题是两个int构成的特征如何作为map的key,第二个问题是,有一些pair在之前的帧中出现过,但是中途中断了,我们如何快速清除?

使用pair

这两个问题我们一个一个来看,先看第一个问题。这个问题很好解决,在C++当中有一个数据结构叫做Pair,它是两个不同类型变量打包成的简单结构体,它可以作为map的key。

具体的用法非常简单,我们用pair<int, int>来声明两个int组成的特征,这里的类型可以根据自己的需要进行修改。当我们需要在map当中使用的时候, 我们采用同样的方式来声明map即可。比如可以写成这样:map<pair<int, int>, int>,由于这样书写比较麻烦,一般acmer会使用define宏定义将它进行简化。比如pair<int, int>通常会简化成pii,表示两个int的pair。

这样的话,我们声明和使用pair就会简单得多。

#define pii pair<int, int>

map<pii, int> mp;
pii p = pii(x, y);
临时map

第二个问题稍稍麻烦一些, 对于一些之前出现的pair,我们需要实时清除。但是我们的map当中只会存储特征连续出现的次数,并没有办法判断每一个特征有没有中断过。

对于这个问题,我们有一个很好的办法,就是使用两个map。第一个map存储的是历史上一直连续出现至今的特征,我们叫它老map。第二个map是临时map,用来储存当前帧加入之后依然持续出现的特征。这样我们只需要在当前帧处理结束之后,用临时的map去更新老map,这样就完成了map中内容的更新。

我这么说可能有一点抽象,大家可以参考一下代码以及注释,会好理解一些。这个技巧也是比赛当中非常常用的技巧,这样通过两个map的来回迭代,就省去了对key的清理工作,这样节省了大量时间。

源码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <cmath>
#include <cstdlib>
#include <string>
#include <map>
#include <set>
#include <algorithm>
#include "time.h"
#include <functional>
#define rep(i,a,b) for (int i=a;i<b;i++)
#define Rep(i,a,b) for (int i=a;i>=b;i--)
#define foreach(e,x) for (__typeof(x.begin()) e=x.begin();e!=x.end();e++)
#define mid ((l+r)>>1)
#define lson (k<<1)
#define rson (k<<1|1)
#define MEM(a,x) memset(a,x,sizeof a)
#define L ch[r][0]
#define R ch[r][1]
#define pii pair<int, int>
using namespace std;
const int N=1000050;
const long long Mod=1000000007;
int x, y;


int main() {
    
    
    int t;
    scanf("%d", &t);
    rep(z, 0, t) {
    
    
        int m;
        scanf("%d", &m);
        map<pii, int> mp, cur;
        int ret = 1;
        int k;
        // 读入m帧
        rep(i, 0, m) {
    
    
            // 临时map记得清空
            cur.clear();
            scanf("%d", &k);
            // 读入k个特征
            rep(j, 0, k) {
    
    
                scanf("%d %d", &x, &y);
                pii p = pii(x, y);
                // 如果在老map中出现过,则更新临时map
                if (mp.count(p)) {
    
    
                    cur[p] = mp[p] + 1;
                    ret = max(ret, cur[p]);
                }else {
    
    
                    cur[p] = 1;
                }
            }
            // 临时map代替老map
            mp = cur;
        }
        printf("%d\n", ret);
    }
    return 0;
}

从代码来看,这道题其实是不难的,也没用到什么高深的算法和数据结构。但是对于新手来说还是很有挑战的,主要是pair的应用很多人不熟悉,然后对于临时map这样的技巧可能也不一定能想得到。更何况这个还是笔试题,真正能在笔试的时候能写上来的就更加困难了,很需要代码能力。

建议大家最好能自己亲手写一下遇到问题之后再看标程,这样的提升最明显。

猜你喜欢

转载自blog.csdn.net/m0_50230964/article/details/114318762