牛客网 - 找工作神器|笔试题库|面试经验|实习招聘内推,求职就业一站解决_牛客网 (nowcoder.com)
3.雀魂启动!
小包最近迷上了一款叫做雀魂的麻将游戏,但是这个游戏规则太复杂,小包玩了几个月了还是输多赢少。
于是生气的小包根据游戏简化了一下规则发明了一种新的麻将,只留下一种花色,并且去除了一些特殊和牌方式(例如七对子等),具体的规则如下:
- 总共有36张牌,每张牌是1~9。每个数字4张牌。
- 你手里有其中的14张牌,如果这14张牌满足如下条件,即算作和牌
- 14张牌中有2张相同数字的牌,称为雀头。
- 除去上述2张牌,剩下12张牌可以组成4个顺子或刻子。顺子的意思是递增的连续3个数字牌(例如234,567等),刻子的意思是相同数字的3个数字牌(例如111,777)
例如:
1 1 1 2 2 2 6 6 6 7 7 7 9 9 可以组成1,2,6,7的4个刻子和9的雀头,可以和牌
1 1 1 1 2 2 3 3 5 6 7 7 8 9 用1做雀头,组123,123,567,789的四个顺子,可以和牌
1 1 1 2 2 2 3 3 3 5 6 7 7 9 无论用1 2 3 7哪个做雀头,都无法组成和牌的条件。
现在,小包从36张牌中抽取了13张牌,他想知道在剩下的23张牌中,再取一张牌,取到哪几种数字牌可以和牌。
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32M,其他语言64M
输入描述:
输入只有一行,包含13个数字,用空格分隔,每个数字在1~9之间,数据保证同种数字最多出现4次。
输出描述:
输出同样是一行,包含1个或以上的数字。代表他再取到哪些牌可以和牌。若满足条件的有多种牌,请按从小到大的顺序输出。若没有满足条件的牌,请输出一个数字0
示例1
输入例子:
1 1 1 2 2 2 5 5 5 6 6 6 9
输出例子:
9
例子说明:
可以组成1,2,6,7的4个刻子和9的雀头
示例2
输入例子:
1 1 1 1 2 2 3 3 5 6 7 8 9
输出例子:
4 7
例子说明:
用1做雀头,组123,123,567或456,789的四个顺子
示例3
输入例子:
1 1 1 2 2 2 3 3 3 5 7 7 9
输出例子:
0
例子说明:
来任何牌都无法和牌
思路:对于接收到的13张牌,依次新增1-9,再引入IsHe函数判断是否当前14张牌是和牌,可和牌则输出该新增的值。
编程实现:递归算法 : 从最小的数字开始尝试,如果能把其当成雀头成员(个数大于等于3且牌的总数不能被3整除),该数字划掉两个,并看余下的数字能否划空如果是刻子成员,该数字划掉三个,并查看余下数字能否划空如果是顺子成员,划掉该值 a ,a+1,a+2,并查看余下数字能否划空如果上述三种尝试都无法划空数组,说明存在数字无法是 雀头、刻子、顺子的成员, 即无法胡牌。上述任何一种情况能划空数组,都可以胡牌)
代码:取自牛客网的评论区nbgao
#include <bits/stdc++.h>
using namespace std;
bool IsHe(map<int,int> M, int t){
if(t<=0)
return true;
while(M[M.begin()->first]==0)
M.erase(M.begin());
map<int,int>::iterator it = M.begin();
int i=it->first, cnt=it->second;
if(t%3!=0 && cnt>=2){
M[i] -= 2;
if(F(M, t-2))
return true;
M[i] += 2;
}
if(cnt>=3){
M[i] -= 3;
if(F(M, t-3))
return true;
M[i] += 3;
}
if(cnt>0 && M[i+1]>0 && M[i+2]>0){
M[i]--;
M[i+1]--;
M[i+2]--;
if(F(M, t-3))
return true;
M[i]++;
M[i+1]++;
M[i+2]++;
}
return false;
}
int main(){
map<int, int> M;
int x;
for(int i=0;i<13;i++){
cin>>x;
M[x]++;
}
vector<int> v;
for(int i=1;i<10;i++){
if(M[i]<4){
M[i]++;
if(IsHe(M, 14))
v.push_back(i);
M[i]--;
}
}
if(v.empty())
cout<<0<<endl;
else{
for(int i=0;i<v.size();i++){
if(i==v.size()-1)
cout<<v[i]<<endl;
else
cout<<v[i]<<" ";
}
}
return 0;
}
4.特征提取
小明是一名算法工程师,同时也是一名铲屎官。某天,他突发奇想,想从猫咪的视频里挖掘一些猫咪的运动信息。为了提取运动信息,他需要从视频的每一帧提取“猫咪特征”。一个猫咪特征是一个两维的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。
现在,给定每一帧的特征,特征的数量可能不一样。小明期望能找到最长的特征运动。
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32M,其他语言64M
输入描述:第一行包含一个正整数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
输出例子:
3
例子说明:特征<1,1>在连续的帧中连续出现3次,相比其他特征连续出现的次数大,所以输出3
思路:求最长的连续<v1,v2>,可用动归的思想。
如何保存状态:先明确是pair<int,int>的值,还要保存当前每个pair<int,int>的次数(方便计算状态)用unordered_map<pair<int, int>, int,hash_pair> cur(hash_pair为自己写的一个hash函数)
状态迭代:新行创建一个新的unordered_map m2,再遍历该行所有pair<int, int>,对于每一个pair的val次数值=上一行的状态m1[pair]+1
#include <iostream>
#include <utility>
#include <vector>
#include <unordered_map>
using namespace std;
struct hash_pair {
template <class T1, class T2>
size_t operator()(const pair<T1, T2>& p) const {
auto hash1 = hash<T1> {}(p.first);
auto hash2 = hash<T2> {}(p.second);
return hash1 ^ hash2;
}
};
int main() {
int n;
cin >> n;
while (n--) {
int num;
cin >> num;
unordered_map<pair<int, int>, int,hash_pair> cur;
int max = 0;
for (int i = 0; i < num; i++) {
int size;
cin >> size;
unordered_map<pair<int, int>, int,hash_pair> temp_map;
for (int i = 0; i < size; i++) {
int temp1, temp2;
cin >> temp1 >> temp2;
pair<int,int> p=make_pair(temp1, temp2);
temp_map[p]=cur[p]+1;
if(temp_map[p]>=max){
max=temp_map[p];
}
}
cur=temp_map;
}
cout<<max<<endl;
}
}