2020阿里笔试——春季实习第一场
没在现场,不知道能过多少样例:
第一题:
一副扑克牌,总共有 A 2 3 4 5 6 7 8 9 每张牌各4张,从中抽取任意张牌,牌可以有四种出法
- 单张牌,比如说 A
- 一对牌,比如说 两个2
- 五张牌顺子,比如说 A 2 3 4 5
- 六张牌连对,比如说 A A 2 2 3 3
现在输入是10种牌每张牌的个数,如 1 1 1 2 2 2 2 2 1 1 ,指的是这10张牌每张的个数,现在问最少出几次能把牌出完。
此时的解答是3次,出3个顺子可以达到目标。
思路:(递归状态搜索)优先找6连,再找5连,再找对子,再找单张;递归查找,递归返回后还原状态。
#include<iostream> #include<vector> #include<map> using namespace std; map<int,int>result; int judge(int leval,int num[]) { if(leval==4||leval==3)//六连或五连 { int count=0; int start=0; for(int i=0;i<10;i++) { if(num[i]!=0) { if(count==0) start=i; count++; } if(num[i]==0) { count=0; start=0; } if(count==leval+2) return start; } } if(leval==2||leval==1) { for(int i=0;i<10;i++) { if(num[i]>=leval) return i; } } return -1; } void dfs(int num[],int floor) { int lable=0; for(int i=0;i<10;i++) { //cout<<num[i]<<" "; if(num[i]!=0) lable=1; } //cout<<endl; if(lable==0) result[floor]++; int tp4=judge(4,num); if(tp4!=-1)//存在六连张 从tp4开始 { for(int i=0;i<6;i++)//改变状态 num[tp4+i]--; dfs(num,floor+1); for(int i=0;i<6;i++)//还原状态 num[tp4+i]++; } int tp3=judge(3,num); if(tp3!=-1)//存在五连张 从tp3开始 { for(int i=0;i<5;i++)//改变状态 num[tp3+i]--; dfs(num,floor+1); for(int i=0;i<5;i++)//还原状态 num[tp3+i]++; } int tp2=judge(2,num); if(tp2!=-1)//存在对子 从tp2开始 { num[tp2]-=2;//改变状态 dfs(num,floor+1); num[tp2]+=2;//还原状态 } int tp1=judge(1,num); if(tp1!=-1)//存在单张 从tp1开始 { num[tp1]-=1;//改变状态 dfs(num,floor+1); num[tp1]+=1;//还原状态 } return ; } int main() { int num[10]; for(int i=0;i<10;i++) cin>>num[i]; dfs(num,0); map<int,int>::iterator it=result.begin(); cout<<it->first<<endl; return 0; }
第二题:
给出一组 n 个字符串。每个字符串都是小写字母组成,且 ascii 码非递减。
求使用这 n 个字符串,最大能拼接出长度为多少的非递减字符串。
(1 <= n <= 10^6)
思路:(dp状态转移)1、用字符串操作比较繁琐,先将每一个输入串转换成这样的格式:开始位置from——结束位置to——期间有几个不同的数字value
2、dp[i]=j 表示处理到第i个输入串时,能最多包含多少个不同的数字
标记数组 record[i]=(node类型)用于记录获得当前dp的情况下 from to的值分别是多少,用于寻找不重合的状态
3、递推公式:dp[i]=max(dp[j]+message[i].value,dp[i-1]); 当前dp值选择 前一个不出现重合的的状态j+当前的状态i 或者是 直接继承前一个状态i-1而不添加当前的状态i
4、特殊关注 :每一个串内可能有相同字符预处理的时候要处理掉;相邻串接到一起时可能当前串的第一个字符和上一个串最后一个字符一样,那么value要删掉一个
#include<iostream> #include<vector> #include<string> #include<stdlib.h> #include<map> using namespace std; struct node { int from; int to; int value; }; int main() { int n; cin>>n; vector<string>input; vector<node>message; while(n--) { string tmp; cin>>tmp; input.push_back(tmp); } for(int i=0;i<input.size();i++) { struct node tmp; string x=input[i]; tmp.from=x[0]-'a'; tmp.to=x[x.size()-1]-'a'; map<char,int>tql; for(int j=0;j<x.size();j++) tql[x[j]]++; tmp.value=tql.size(); message.push_back(tmp); } //数据格式:from开始的位置 to结束的位置 value有几个不同的单词 /*for(int i=0;i<message.size();i++) { struct node tmp; tmp=message[i]; cout<<tmp.from<<" "<<tmp.to<<" "<<tmp.value<<endl; }*/ struct node tmp; tmp=message[0]; vector<node>record(message.size()+1,tmp);//record记录占用的起始位置 vector<int>dp(message.size()+1,0);//dp存最大字符个数 dp[0]=tmp.value; for(int i=1;i<message.size();i++) { struct node messg=message[i];//当前 int j; for(j=i-1;j>=0;j--)//寻找前一个不重合的状态 { struct node recrd=record[j]; if(recrd.to<=messg.from) { break; } } //dp[i]=max(dp[j]+message[i].value,dp[i-1]);//递推公式 if(dp[j]+message[i].value>dp[i-1]) { dp[i]=dp[j]+message[i].value; record[i]=message[i]; if(message[j].to==message[i].from)//有重合字符时删掉一个 dp[i]--; } if(dp[j]+message[i].value<dp[i-1]) { dp[i]=dp[i-1]; record[i]=message[i-1]; } } /*for(int i=0;i<message.size();i++) { cout<<dp[i]<<" "; }*/ cout<<dp[message.size()-1]<<endl; return 0; } /* 测试: 4 abc deh dddd xz 4 abc ccc xyz uvwx 5 aaa bcf bcdef zzz zzz */
附:模拟题2018阿里
八卦阵,矩阵中有八个不相连的区域,每个区域由相邻的大于零的数字组成,区域与区域之间由零隔开,求这些区域的和的最大值和最小值是多少
#include<iostream> #include<vector> #include<string> #include<stdlib.h> #include<map> using namespace std; void dfs(int i,int j,int m,int n,vector<vector<int> >&mp,vector<vector<int> >&lable,int& count) { if(i<0||i>=m||j<0||j>=n||lable[i][j]!=0||mp[i][j]==0) return ; lable[i][j]=1; count+=mp[i][j]; dfs(i,j-1,m,n,mp,lable,count); dfs(i,j+1,m,n,mp,lable,count); dfs(i-1,j,m,n,mp,lable,count); dfs(i+1,j,m,n,mp,lable,count); return; } int main() { int m,n,max=0; cin>>m>>n; vector<vector<int> > mp(m+1,vector<int>(n+1,0)); vector<vector<int> > lable(m+1,vector<int>(n+1,0)); for(int i=0;i<m;i++) for(int j=0;j<n;j++) cin>>mp[i][j]; for(int i=0;i<m;i++) { for(int j=0;j<n;j++) { if(lable[i][j]==0&&mp[i][j]!=0) { int count=0; dfs(i,j,m,n,mp,lable,count); max=(max>count?max:count); } } } cout<<max<<endl; return 0; } /* 2 6 1 2 0 5 6 0 3 4 0 7 8 0 */
寻找有向图中这样的一个结点,从这个结点出发的路径数量是最多的
#include<iostream> #include<vector> #include<string> #include<stdlib.h> #include<map> using namespace std; #define MAX 999999 void dfs(int i,vector<vector<int> >&mmp,vector<vector<int> >&lable,int &count) { for(int k=0;k<mmp[i].size();k++) { if(lable[i][mmp[i][k]]!=1) { count++; lable[i][(mmp[i][k])]=1; dfs(mmp[i][k],mmp,lable,count); } } return ; } int main() { int m,n,max=0; cin>>m;//m条边 vector<vector<int> >mmp(m+1,vector<int>()); for(int i=1;i<=m;i++) { int x,y; cin>>x>>y; vector<int>tmp=mmp[x]; tmp.push_back(y); mmp[x]=tmp; } for(int i=1;i<=m;i++) { for(int j=0;j<mmp[i].size();j++) cout<<mmp[i][j]; cout<<endl; } for(int i=1;i<=m;i++) { int count=0; vector<vector<int> >lable(m+1,vector<int>(m+1,0)); dfs(i,mmp,lable,count); max=max>count?max:count; } cout<<max<<endl; return 0; } /* 6 1 6 1 2 1 5 2 3 3 4 4 5 6 5 3 5 4 3 1 3 4 4 2 2 1 */
附:美团
1.扎金花是一种非常受欢迎的纸牌游戏。而在游戏界有一种于扎金花类似的玩法,叫做扎银花。 相比于扎金花的牌型多变,扎银花就简单多了,虽然同样是三张牌比大小,在扎银花的规则里只需要把三张牌的点数相加再进行大小比较即可,点数大的人获胜。 今天我们玩的不是扑克牌,而是一种取值范围在1-10^9以内的简单牌,两个人一开始各自有n张牌,他们会想办法组合出最大的牌,请你计算出获胜的一方的三张牌的点数之和。 2.给出一个长度为n的由正整数构成的序列,你需要从中删除一个正整数,很显然你有很多种删除方式,你需要对删除这个正整数以后的序列求其最长上升子串,请问在所有删除方案中,最长的上升子串长度是多少。 这里给出最长上升子串的定义:即对于序列中连续的若干个正整数,满足a_{i+1}>a_i,则称这连续的若干个整数构成的子串为上升子串,在所有的上升子串中,长度最长的称为最长上升子串。 3.现在一共有n个任务可以完成。对于每个任务,都有k个子任务可以做。并且第 i 个子任务需要花费的时间是 ti 。我们可以认为一个子任务需要的时间只和这个子任务是第几个子任务有关,而不和这是属于哪个任务有关。也就是说n个任务的第 i 个子任务需要的时间都是一样的。 每个任务都只可以完成一次,同时每个子任务也只能完成一次,任何任务都不能重复完成。 每当你完成一个子任务你会获得p分,而当你完成一个任务的k个子任务后,你会获得额外的q分,也就是说你会获得pk+q分。 现在你一共有m的时间,你需要求出最大的得分。 4.晨晨是个爱跑步的孩子,这一天,他准备跑正好k米。他所在的城市的道路可以看做n个点,m条无向边组成的图,每条边有一个固定的长度。 晨晨有强迫症,他跑步前往一个目的地一定要走最短路(当然有多条最短路就可以随意选择了)。 晨晨希望知道,他正好跑k米能走到的目的地的个数。注意,目的地可能在图中的点和边上,且该目的地距离晨晨的起点的最短路正好k米。 若k大于所有路径之和自然不存在这样的目的地,输出结果自然为0。 5.某个序列的最长不下降子序列的定义为将这个序列去除最少的数,使得剩下的每一个数都大于等于他自身前面的数。比如,1,0,0,1,1的最长不下降子序列为0,0,1,1,其中去除了第一个1,剩下的数0,0,1,1后面的数都大于等于前面的数。 现在有一个特殊的序列,这个序列中所有的数都是0或者1。你需要按照题目所给的顺序完成两种任务: 1.将某段区间的0变为1,1变为0 2.询问整段序列的最长不下降子序列长度。 每一个操作进行后都会对序列造成改变,这意味着整个序列会不停的发生变化。