头条2018实习生笔试

输入解释:5是有5个用户,下一行输入5个用户对某一类文章的喜好程度k。3是有多少个查询,后面3行如2,4,5表示第二个用户到第4个用户之间有多少个用户的喜好程度为5. 输出这个数。

这个题的核心是用了一个map,key是每一种喜好程度值k,然后把同一个k的用户id放在一个vector里。即map<int, vector<int>>,每一个查询直接在要查询的喜好程度k相应的用户id数组中找。

#include <iostream>
#include <vector>
#include <map>
using namespace std;
int main()
{
    int n;
    cin >> n;
    vector<int> f(n, 0);
    map<int, vector<int>> m;
    for(int i = 0; i < n; ++i)
    {
        cin >> f[i];
        m[f[i]].push_back(i+1);
    }
    //查询
    int q;
    cin >> q;
    for(int i = 0; i < q; ++i)
    {
        int start, end, ques;
        cin >> start >> end >> ques;
        int count = 0;
        for(int j = 0; j < m[ques].size(); ++j)
        {
            if(m[ques][j] >= start && m[ques][j] <= end)
            {
                count++;
            }
        }
        cout << count << endl;
    }
}


这个题我也是用了同样的map,颜色号为key,把同一个颜色的不同珠子的序号放在一个vector里,即map<int, vector<int>>。对每一种颜色查看是否符合要求。先是两两检查,然后头尾检查,用到取模。

注意这个题我一开始把首位检查写在两两检查的for循环外面了,导致有些两两不符合要求首位也不符合要求的颜色被加了两次,调了很久才发现。。。

#include <iostream>
#include <vector>
#include <map>
using namespace std;
#include <fstream>
int main()
{
#ifdef debug
    ifstream cin("C:\\input.txt");
    ofstream cout("C:\\output.txt");
#endif // debug
    int res = 0;
    int n, m, c;
    cin >> n >> m >> c;
    map<int, vector<int>> mm;
    string s;
    for(int i = 0; i < n; ++i)
    {
        int colornum;
        cin >> colornum;
        for(int j = 0; j < colornum; ++j)
        {
            int x;
            cin >> x;
            mm[x].push_back(i+1);
        }
    }
    for(int i = 1; i <= c; ++i)
    {
        for(int j = 1; j <= mm[i].size()-1; ++j)
        {
            if(mm[i][j] <= mm[i][j-1] + m - 1)
            {
                res++;
                break;
            }
            if(j == mm[i].size()-1)
            {
                if((mm[i][j] + m) % n > mm[i][0])
                {
                    res++;
                    break;
                }
            }
        }

        //  错误原因:当前后两两比较的时候,如果发现错了,应该马上看下一个颜色,
        //  如果把头尾比较放在for的外面,那么在前后和头尾都有错的情况下,会重复加到结果集里
        //int j = mm[i].size()-1;
        //if(j >= 2)
        //{
        //    if((mm[i][j] + m) % n > mm[i][0])
        //    {
        //        res++;
        //        cout << i << "," << j << "-----(2)" << endl;
        //        for(int k = 0; k < mm[i].size(); ++k)
        //        {
        //            cout << mm[i][k] << " ";
        //        }
        //        cout << endl;
        //    }
        //}

    }
    cout << res << endl;
}

这是一个动态规划题,对我来说有难度。我的做法还是定义了一个map,这次是让字母为key,把字母出现过的下标放在vector里。即map<char, vector<int>>。

例如a出现过的下标为0,4,5,9.要找出不少于m次操作的情况下,最多能把多少个a聚在一起,假设答案为res。换个思路想,找出把2个a聚在一起的最少操作次数,把3个a聚在一起的最少操作次数,4个a聚在一起的最少操作次数,len个a聚在一起的操作次数。如果这些操作次数小于m,那么可以更新res为len。

接下来的问题是,怎样找出把len个a聚在一起的最少操作次数?这里用动态规划。设下标的数组为pos[4],dp[4][4],其中dp[i][j]表示把pos[i]到pos[j]的a聚集起来需要的最少操作次数,初始化为0.  要操作次数最少,肯定是往中间聚。已知聚起来的长度为len。那么有dp[i][i+len-1] = dp[i+1][i+len-2] + pos[i+len-1]-pos[i]-len+1.

这个表达式是什么意思?比如现在要求出4个a聚集起来最少操作次数。已经知道把中间两个聚集起来的次数为dp[i+1][i+len-2],然后就是要把下标为0的a和下标为9的a聚到中间去。这两个a加起来需要移动的次数是,他俩中间的所有数字有多少个,减去已经排好了的a的个数。即pos[i+len-1] - pos[i] - 1 - (len - 2)。

#include <iostream>
#include <vector>
#include <map>
using namespace std;
int main()
{
    string s;
    int m;
    cin >> s >> m;
    int n = s.size();
    int res = 0;
    map<char, vector<int>> mm;
    for(int i = 0; i < n; ++i)
    {
        mm[s[i]].push_back(i);
    }
    for(auto a : mm)
    {
        //cout << a.first << endl;
        int idxnum = a.second.size();
        vector<vector<int>> dp(idxnum, vector<int>(idxnum, 0));
        for(int len = 2; len <= idxnum; ++len)
        {
            for(int i = 0; i + len - 1 < idxnum; ++i)
            {
                dp[i][i + len - 1] = dp[i+1][i+len-2] + (a.second)[i+len-1]
                                     -(a.second)[i] - len + 1;
                if(dp[i][i+len-1] <= m)
                    res = max(len, res);
            }
        }
    }
    cout << res << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值