腾讯2020校园招聘-后台-不完整题解

腾讯2020校园招聘-后台-题解

整套试卷传送门

一、压缩算法(栈模拟)

考点:数据结构——栈模拟
传送门

C++11语法版
#include <bits/stdc++.h>
using namespace std;
 
string s;

void solve()
{
	int i = 0;
	int len=s.size();
	
    while(i < len)
	{
        if(s[i] == ']')
		{
            int j = i;//j用来向前寻找与]相匹配的[
            
            int location=0;//location用来记录'|'所在位置
            
            while(s[j] != '[')
			{
                if(s[j] == '|')
                {
                	location = j;
				}
				
                --j; 
            }
            
            //Stoi是C++11的,C++98会报错 
            int len = stoi(s.substr(j+1,location-j));
            
            string s1 = s.substr(location+1 , i-location-1);
            
            string s2;
            for(int si = 0; si < len; si++)
			{//将识别到的括号内容进行解码
                s2 += s1;
            }
            
            s = s.replace(j,i-j+1,s2);
            i = j;//替换后i所指向的内容变化,从替换部分的头开始再寻找
        }
        
        ++i;
    }
}


int main()
{
    while(cin>>s) 
    {
    	solve();
    	cout<<s<<endl;
	}
    
}

二、逛街(单调栈)

考点:数据结构——单调栈

解法:我用了2个单调栈来辅助,一个单调递减,一个单调递增

#include<bits/stdc++.h>
using namespace std;

const int maxn=100000+5;
int solve[maxn];

int leftSum[maxn];
int rightSum[maxn];


//左边的单调递减栈 
void leftStack(int n)
{
	//以当前数字结尾的单调递减的栈的长度保存 
	stack<int> st;
	
	st.push(solve[0]);
	leftSum[0]=1;
	
	for(int i=1;i<n;++i)
	{
		int tag=1;//表示未求出leftSum
		 
		while(!st.empty())
		{
			if(st.top()>solve[i])
			{
					
				leftSum[i]=st.size()+1;
				st.push(solve[i]);
				tag=0; 
				break;
			}
			else
			{
				st.pop();
			}
		} 
		
		
		if(tag)
		{
			leftSum[i]=1;
			st.push(solve[i]);
		}
		
	}
	
}


//右边的单调递增栈 
void rightStack(int n)
{
	//以当前数字开头的单调递增的栈的长度保存 
	stack<int> st;
	
	st.push(solve[n-1]);
	rightSum[n-1]=1;
	
	for(int i=n-1;i>=0;--i)
	{
		int tag=1;//表示未求出rightSum
		 
		while(!st.empty())
		{
			if(solve[i]<st.top())
			{
					
				rightSum[i]=st.size()+1;
				st.push(solve[i]);
				tag=0; 
				break;
			}
			else
			{
				st.pop();
			}
		} 
		
		
		if(tag)
		{
			rightSum[i]=1;
			st.push(solve[i]);
		}
		
	}
}


int main()
{
	int n;
	while(~scanf("%d",&n))
	{
		for(int i=0;i<n;++i)
		{
			scanf("%d",&solve[i]);
		}
		
		leftStack(n);
		rightStack(n);
		
		if(1==n)
		{
			printf("%d\n",1);
		}
		else if(2==n)
		{
			printf("2 2\n");
		}
		else
		{
			solve[0]=rightSum[1]+1;
			solve[n-1]=leftSum[n-2]+1;
			for(int i=1;i<n-1;++i)
			{
				//左边栈+右边栈+自己本身 
				solve[i]=leftSum[i-1]+rightSum[i+1]+1;
			}
			
			for(int i=0;i<n;++i)
			{
				if(i!=(n-1))
				{
					printf("%d ",solve[i]);
				}
				else
				{
					printf("%d\n",solve[i]);
				}
			}
		}
	}
	
	return 0;
}

三、逆序对(归并排序变体+双指针)

我首先想到的是《剑指offer》上好像有类似题目
还记得这个还可以用“”树状数组”来解决
传送门
未AC

您的代码已保存
运行超时:您的程序未能在规定时间内运行结束,
请检查是否循环有错或算法复杂度过大。
case通过率为60.00%

通过百分之60的

#include<bits/stdc++.h>
using namespace std;

const int maxn=1000000+5;
int solve[maxn];
int n;
int tmp[maxn];


//分组处理 
void solution(int temp)
{
	if(0==temp)
	{
		return ;
	}
	
	temp=(int)pow(2,temp); 
	
	int num=n/temp;
	
	int i=0;
	int j=temp;
	int loop=num;
	while(loop--)
	{
		reverse(solve+i,solve+j);
		i=j;
		j+=temp;
	}
}


int mergeSort(int nums[], int tmp[], int l, int r) 
{
        if (l >= r) 
		{
            return 0;
        }

        int mid = (l + r) / 2;
        int inv_count = mergeSort(nums, tmp, l, mid) + mergeSort(nums, tmp, mid + 1, r);
        int i = l, j = mid + 1, pos = l;
        while (i <= mid && j <= r) 
		{
            if (nums[i] <= nums[j]) 
			{
                tmp[pos] = nums[i];
                ++i;
                inv_count += (j - (mid + 1));
            }
            else 
			{
                tmp[pos] = nums[j];
                ++j;
            }
            ++pos;
        }
        
        for (int k = i; k <= mid; ++k) 
		{
            tmp[pos++] = nums[k];
            inv_count += (j - (mid + 1));
        }
        
        for (int k = j; k <= r; ++k) 
		{
            tmp[pos++] = nums[k];
        }
        
        copy(tmp+ l, tmp+ r + 1, nums+ l);
        
        return inv_count;
}




//求逆序数 
int myCount()
{
	//因为这个归并顺路给我排序了。。 
	int nums[n];
	for(int i=0;i<n;++i)
	{
		nums[i]=solve[i];
	}
    return mergeSort(nums, tmp, 0, n - 1);
} 



int main()
{
	
	while(~scanf("%d",&n))
	{
		n=(int)pow(2,n); 

		
		for(int i=0;i<n;++i)
		{
			scanf("%d",&solve[i]);
		}
		
		int m;
		scanf("%d",&m);
		for(int i=0;i<m;++i)
		{
			int temp;
			scanf("%d",&temp);
			solution(temp);
			printf("%d\n",myCount());
		}
		
	}
	
	return 0;
} 

四、假期(动态规划)

  • 第一眼,看上去,感觉应该考dp或者贪心或者DFS
    但是,不知道咋弄转移方程
    贪心也被自己用case否决了
    DFS,选上面和选下面,分别对应1和0
  • 最终,我没有想出动态规划解法,主要是自己想用DFS,递归深度太深
  • 维特比算法 (Viterbi algorithm) 是机器学习中应用非常广泛的动态规划算法,在求解隐马尔科夫、条件随机场的预测以及seq2seq模型概率计算等问题中均用到了该算法。实际上,维特比算法不仅是很多自然语言处理的解码算法,也是现代数字通信中使用最频繁的算法。

隐马尔科夫…想起了“计算材料学”课程中,好像有提到过这个?
还有随机游动。。。自我吐槽。。

算法极其优美,状态转移如下

定义f[i][j] 为截止到前i天, 第i天做选择j时的最小休息日
算法的状态转移,如下图
图片来源
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;

const int maxn=100000+5;
int one[maxn];
int two[maxn];
int n;//设置为全局 

int solve(int company[], int gym[]) 
{
	
    // j: 0休息 1工作 2健身
    //f[i][j] 截止到前i天, 第i天做选择j时的,最小休息日
    //n行,3列 
    vector<vector<int> > f(n, vector<int>(3, 0x3f3f3f3f));
    
    
    //因为“休息”可以转移到任何3种状态? 
	//所以截止到前0天,第0天做选择0(休息)的最小休息日是1 
    f[0][0] = 1;
    
    if(1==company[0]) 
	{
		//截止到前0天,第0天做选择1(工作)的最小休息日是0  
    	f[0][1] = 0;
	}
	
    if(1==gym[0])
    {
    	//截止到前0天,第0天做选择2(锻炼)的最小休息日是0  
    	f[0][2] = 0;
	}
    
    
    // 有三个选择, 分别推导
    
    
    for(int i=1;i<n;i++) 
	{
        
        //只有公司营业才能选择上班
        if(1==company[i] ) 
		{
            //因为不会连续两天工作或锻炼
            //“工作”状态只能由 “休息” 或者“锻炼”转移过来 
            f[i][1] = min(f[i-1][0], f[i-1][2]);
        }

        // 只有健身房营业才能选择健身
        if(1==gym[i]) 
		{
			//因为不会连续两天工作或锻炼
			//“健身”状态只能由 “工作” 或者“休息”转移过来 
            f[i][2] = min(f[i-1][0], f[i-1][1]);
        }
        
        // 无条件可以选择休息,所以,需要在前面的基础上+1 
        f[i][0] = min(f[i - 1][2], min(f[i - 1][0], f[i - 1][1])) + 1;
        
    }
    
    return min(f[n-1][0], min(f[n-1][1], f[n-1][2]));
}



int main()
{
	
	while(~scanf("%d",&n))
	{
		for(int i=0;i<n;++i)
		{
			scanf("%d",&one[i]);
		}
		
		for(int i=0;i<n;++i)
		{
			scanf("%d",&two[i]);
		}
		
		printf("%d\n",solve(one,two));
	}
	
	return 0;
}

五、视野争夺(动态规划/贪心)

第一感觉,好像考点是“线段树”?还是“贪心”?

有点像CCF/CSP中有道求覆盖区间的

下面算法用的“贪心算法”
我还想用的是deque和自建cmp
结构体
模拟这个,但是直接用pair了

#include<bits/stdc++.h> 
using namespace std;
  
int main()
{
	
    int n,L;
    
	while(cin>>n>>L)
	{
		vector<pair<int,int> > vec(n);
		
	    for(int i=0;i<n;++i) 
		{
			cin >> vec[i].first >> vec[i].second;
		}
		
		//以first进行排序 
	    sort(vec.begin(),vec.end());
	    
	    if(vec[0].first>0) 
		{
			cout<< -1;
			continue;
		}
		

	    int right = 0;//右指针 
	    
	    int res = 0;//个数 
	    
	    int loop= 0;//循环遍历 
	    while(loop<n)
		{
			//当前边界的右边最大值 
	        int r = right;
	        
	        //获取左边区间可以补充进来,并且,右边最大(贪心)
			//并且更新 
	        for(	;loop<n&&vec[loop].first<=r; ++loop)
			{
	            right=max(right,vec[loop].second);
	        }
	        
	        ++res;
	        
	        if(right>=L) 
			{
				cout<< res;
				break;
			}
			
	        if(loop>=n||vec[loop].first>right) 
			{
				cout<<-1<<endl;
				break;
			}
	    }
	    
	    
	}
	
	
   
    return 0;
   
}

后面发现,还有大佬的代码很精简
传送门

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值