#阿里笔试2020# 看到了这里都在讨论3.23的两道题,但是看到大家解法好像都不是太好,我分享一下我的解题方法吧。
第一题 求排列组合。在n个人中可以取任意数目的人数组成一队,并且可以在其中选队长,问一共有多少种组合方式,最后的结果模10^9+7.
这题更像是一个数学题。由组合公式可知 C(m,N)=n!/((n-m)!+m!)每个组合中队长的选举可能有m种那么每种人数组合就是C(m,N)*m
举个例子 假设N=4
则一共有 C(1,4)*1+C(2,4)*2+C(3,4)*3+C(4,4)*4 =32种
那么N个人一共有n+n*n(n-1)+n*(n-1)(n-2)+n(n-1)(n-2)(n-3)……+n*n(n-1)+n种,值得注意的是由于每个因子都乘了一个m 所以和组合公式有一点点差别。原本的本来是C(m,n)=C(n-m,n),现在是C(m+1,n)=C(n-m,n)
两边完全对称,我们只需要算到n/2,然后乘以二就可以了。则上述公式可以提公因式得:n(1+(n-1)(1+(n-2)(1+(n-3)(……1+(n-n/2)(1)))))*2。这里又有一个小陷阱。奇数和偶数的公式是不一样的,偶数当然两边完全一致,直接乘2就好了,奇数中间只有一个,不能乘2,所以我们最后加上中间那个组合就可以了。
C++代码如下:
int howMany(int n) {
int result = 0;
for (int i = n/2+1; i <= n ; i++)
{
result = (result + 1)*i;
}
if (n % 2 == 0)//偶数 直接对半*2
{
result= result*2;
}
else {//奇数 偶数的基础上+上n/2的排列组合
int temp1=1,temp2=1;
for (int i = n / 2 + 1; i <= n; i++) {
temp1 *= i;
}
for (int i = 1; i <= n / 2 + 1; i++) {
temp2 *= i;
}
result = temp1 / temp2 + result;
}
return result;
}
第二题写不下了 见评论
第一题 求排列组合。在n个人中可以取任意数目的人数组成一队,并且可以在其中选队长,问一共有多少种组合方式,最后的结果模10^9+7.
这题更像是一个数学题。由组合公式可知 C(m,N)=n!/((n-m)!+m!)每个组合中队长的选举可能有m种那么每种人数组合就是C(m,N)*m
举个例子 假设N=4
则一共有 C(1,4)*1+C(2,4)*2+C(3,4)*3+C(4,4)*4 =32种
那么N个人一共有n+n*n(n-1)+n*(n-1)(n-2)+n(n-1)(n-2)(n-3)……+n*n(n-1)+n种,值得注意的是由于每个因子都乘了一个m 所以和组合公式有一点点差别。原本的本来是C(m,n)=C(n-m,n),现在是C(m+1,n)=C(n-m,n)
两边完全对称,我们只需要算到n/2,然后乘以二就可以了。则上述公式可以提公因式得:n(1+(n-1)(1+(n-2)(1+(n-3)(……1+(n-n/2)(1)))))*2。这里又有一个小陷阱。奇数和偶数的公式是不一样的,偶数当然两边完全一致,直接乘2就好了,奇数中间只有一个,不能乘2,所以我们最后加上中间那个组合就可以了。
C++代码如下:
int howMany(int n) {
int result = 0;
for (int i = n/2+1; i <= n ; i++)
{
result = (result + 1)*i;
}
if (n % 2 == 0)//偶数 直接对半*2
{
result= result*2;
}
else {//奇数 偶数的基础上+上n/2的排列组合
int temp1=1,temp2=1;
for (int i = n / 2 + 1; i <= n; i++) {
temp1 *= i;
}
for (int i = 1; i <= n / 2 + 1; i++) {
temp2 *= i;
}
result = temp1 / temp2 + result;
}
return result;
}
第二题写不下了 见评论
全部评论
第二题:
小明在学旋律,每段旋律都可以用字符串来表示,并且旋律的每个
字符的ASCALL码递增的
比如以下4段旋律 :
aaa
bcd
bcdef
zzz
现在就是要求,小明能够吧这些旋律拼接起来组成最长的旋律。
比如上述例子输出 11 最长的旋律为 aaabcdefzzz
这题看似简单,不就排个序,然后把第一个字符串的末尾字符小于第二个字符串首字符的两个字符串拼接起来就好了嘛,但是字符串之间可能出现交叉,比如 aaa bcd cdef 肯定是拼接cdef好而不是bcd。那么如何解决呢,这种最优解或最大路径问题,一般就要用到回溯法或者动态规划了。
回溯法就是走迷宫,遍历完所有路径,找出最优的那个路径就行
void nextMelody(vector<string> melodies, int n, int curLength) {
if (n == melodies.size()) {
return;
}
string curMelody = melodies[n];
curLength += curMelody.length();
maxLength = max(maxLength, curLength);
char curMelodyLastChar = curMelody[curMelody.length() - 1];
for (int i = n + 1; i < melodies.size(); i++) {
char nextMelodyFirstChar = melodies[i][0];
int cmp = curMelodyLastChar - nextMelodyFirstChar;
if (cmp <= 0 && curLength + lengthLeft[i] > maxLength) {
nextMelody(melodies, i, curLength);
}
}
}
这是一个递归的回溯剪枝,lengthLeft[]和maxLength 是全局变量,分别用于纪录原数组中还剩的字符串总长度和目前以查找的最优路径,剪枝的条件前str[末]<str[头]且当前最优路径+还剩路径>maxLength (否则就不考虑了,肯定最优已经出来了)
动态规划和回溯差不多 不过可以从后往前,这样可以节省很多重复的开销,不过要开辟N的额外空间。时间效率上都差不多
int dynamicSolver(vector<string>& str) {
//从后往前
int len = str.size();
//用于纪录路径最大和
int *dp=new int[len];
memset(dp, 0, sizeof(dp[0]));
int maxlength=0;
for (int i = len - 1; i >= 0; i--) {
for (int j = i; j < len ; j++)
{
if (i == len - 1)
{
dp[i] = str[i].size();
if (dp[i] > maxlength)
maxlength = dp[i];
}
else if (str[i][str[i].size() - 1] <= str[j][0])
{
dp[i] = max(dp[i], (int)(dp[j] + str[i].size()));
if (dp[i] > maxlength)
maxlength = dp[i];
}
}
}
return maxlength;
}
对了 因为我不是面试者,我只是刷一刷题 ,我只是重视思路,有些细节没注意到,比如说第一题要mod 10^9 第二题是求路径 我求的总和,思路应该是没问题的
第一题我也是这个思路,但是我通过率0。。
听说第一题可以推公式,n*2^(n-1)
相关推荐
点赞 评论 收藏
分享
01-05 19:07
北京大学 算法工程师 野猪不是猪🐗:把你的学校加黑,加粗,斜体,下划线,描边,内阴影,内发光,投影,外发光,再上渐变色,居中,放大到最大字号,再把简历里其它内容删了,就行了
点赞 评论 收藏
分享