腾讯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;
}
后面发现,还有大佬的代码很精简
传送门