头条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;
}