[笔试练习]美团笔试题

1.请设计一个算法,给一个字符串进行二进制编码,使得编码后字符串的长度最短。

输入描述:
每组数据一行,为待编码的字符串。保证字符串长度小于等于1000。


输出描述:
一行输出最短的编码后长度。

输入例子:
MT-TECH-TEAM

输出例子:
33

分析:哈夫曼编码的过程,用一个最小堆实现,将当前最小的两个数取出来,求和后继续推入堆中。

代码:

#include <iostream>
#include <queue>
#include <algorithm>
#include <functional>
#include <string>
using namespace std;
int main() {
	string s;
	while (getline(cin, s)) {
		int ret=0;
		int n = s.size();
		sort(s.begin(), s.end());
		int cnt = 0;
		priority_queue<int, vector<int>, greater<int> > heap;
		for (int i = 0, j; i < n;) {
			j = i;
			while (j < n && s[j] == s[i]) ++j;
			heap.push(j - i);
			i = j;
			++cnt;
		}
		for (int i = 0; i < cnt-1; i++) {
			int a = heap.top();
			heap.pop();
			int b = heap.top();
			heap.pop();
			heap.push(a+b);
			ret += a + b;
			

		}
		cout << ret << endl;

	}
	return 0;
}

2.对于一个由0..n的所有数按升序组成的序列,我们要进行一些筛选,每次我们取当前所有数字中从小到大的第奇数位个的数,并将其丢弃。重复这一过程直到最后剩下一个数。请求出最后剩下的数字。

输入描述:
每组数据一行一个数字,为题目中的n(n小于等于1000)。


输出描述:
一行输出最后剩下的数字。

输入例子:
500

输出例子:
255

分析(转的):因为是从0开始,所以第一轮移走的是二进制下最右边为0的位置(从0开始的偶数位置)上的数,然后我们发现第二轮各个number的位置等于number/2,即从number位置到number>>1位置,这时候我们依然移走二进制下最右边为0的位置(1(01)  5(101) 9(1001) ……它们第二轮对应的位置是0, 2, 4),最后剩一个数肯定是0到n中二进制下1最多的那个数,因为它每次的位置都是奇数位置。

代码:

#include <iostream>
using namespace std;
int main(){
    int n;
    while(cin>>n){
        int b=1;
        while(b<n){
            b<<=1;
        }
        b=(b>>1)-1;
        cout<<b<<endl;
    }
    return 0;
}

3.

有一个二维数组(n*n),写程序实现从右上角到左下角沿主对角线方向打印。

给定一个二位数组arr及题目中的参数n,请返回结果数组。

测试样例:
[[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]],4
返回:[4,3,8,2,7,12,1,6,11,16,5,10,15,9,14,13]

分析:矩阵打印题,写的有点麻烦了。

代码:

class Printer {
public:
    vector<int> arrayPrint(vector<vector<int> > arr, int n) {
        vector<int> ret;
        int k=0;
        int i=0;
        int j=n-1;
        while(k<n){
            
            for(int h=0;h<=k;h++){
                ret.push_back(arr[i+h][j+h]);
                
            }
            j--;
            k++;
        }
        k=k-2;
        j++;
        i++;
        while(k>=0){
            for(int h=0;h<=k;h++){
                ret.push_back(arr[i+h][j+h]);
            }
            i++;
            k--;
        }

        return ret;
    }
};

4.

在股市的交易日中,假设最多可进行两次买卖(即买和卖的次数均小于等于2),规则是必须一笔成交后进行另一笔(即买-卖-买-卖的顺序进行)。给出一天中的股票变化序列,请写一个程序计算一天可以获得的最大收益。请采用实践复杂度低的方法实现。

给定价格序列prices及它的长度n,请返回最大收益。保证长度小于等于500。

测试样例:
[10,22,5,75,65,80],6
返回:87

分析:leetcode原题。。动态规划,买卖次数小于等于2,那么对于每一个点,找到这个点之前买卖一次能获得的最大收益,以及之后买卖一次获得的最大收益,找到这个前后和的最大值。

代码:

class Stock {
public:
    int maxProfit(vector<int> prices, int n) {
        vector<int> preprofit(n,0);
        vector<int> postprofit(n,0);
        int curMin=prices[0];
        int pre=0;
        for(int i=1;i<n;i++){
            curMin=min(curMin,prices[i]);
            pre=max(pre,prices[i]-curMin);
            preprofit[i]=pre;
        }
        int curMax=prices[n-1];
        int post=0;
        for(int i=n-2;i>=0;i--){
            curMax=max(curMax,prices[i]);
            post=max(post,curMax-prices[i]);
            postprofit[i]=post;
        }
        int ret=0;
        for(int i=0;i<n;i++){
            ret=max(ret,preprofit[i]+postprofit[i]);
        }
        return ret;
    }
};


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值