恭喜发现宝藏!微信搜索公众号【TechGuide】关注更多新鲜好文和互联网大厂的笔经面经。
作者@TechGuide【全网同名】
点赞再看,养成习惯,您动动手指对原创作者意义非凡🤝
文章目录
提示
- 腾讯有多套试卷,题目不一样,但难度相近,题型相似。
- 以下是用c++实现,理解思想即可。
第一道:开锁(100%)
题目描述
有n 把钥匙,m 个锁,每把锁只能由一把特定的钥匙打开,其他钥匙都无法打开。一把钥匙可能可以打开多把锁,钥匙也可以重复使用。
对于任意一把锁来说,打开它的钥匙是哪一把是等概率的。但你无法事先知道是哪一把钥匙,只能进行尝试。
已知每次尝试用第i把钥匙打开第j把锁会消耗的时间a ij 秒。
问最优策略下打开所有锁的总期望时间是多少秒。
输入描述
第一行两个以空格分隔的正整数n,m。
接下来m行每行m个空格分隔的正整数aij。
1<=n,m,aij <=500
输出描述
输出一个小数代表答案,你的答案会被认为是正确的当且仅当你的答案与正确答案的绝对误差或相对误差不超过10-6。
参考代码:
开锁 求期望(排序+累计和)
CPP版本(100%)
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<vector<int>> a(m, vector<int>(n));
for (int j = 0; j < n; ++j) {
for (int i = 0; i < m; ++i) {
cin >> a[i][j];
}
}
double ans = 0;
for (int i = 0; i < m; ++i) {
sort(a[i].begin(), a[i].end());
for (int j = 0; j < n; ++j) {
ans += (n - j) * a[i][j];
}
}
printf("%.6lf\n", ans / n);
return 0;
}
// 关注TechGuide! 大厂笔经面经闪电速递!
第二道: 勇闯币圈(100%)
题目描述
最近以比特币为代表的数字货币市场非常动荡,聪明的小明打算用马尔科夫链来建模股市。如图所示,该模型有三种状态:“行情稳定”,“行情大跌”以及“行情大涨”。每一个状态都以一定的概率转化到下一个状态。比如,“行情稳定”以0.4的概率转化到“行情大跌”的状态。
不妨设“行情稳定”,“行情大跌”以及“行情大涨”分别为状态0,状态1和状态2。我们可以定义概率转移矩阵P,其中P(i,j)的数值表示的是状态j转移到状态i的概率。如图所示的概率转移矩阵为:
有了概率转移矩阵P,我们只要知道一个初始状态 ,我们就容易可以知道第 t 步三种状态的概率了。由此可以知道行情什么时候大涨大跌,从而发家致富,赢取白富美,走向人生巅峰。比如我们想知道第1步之后“行情大跌”的概率,那么由全概率公式和马尔科夫链的性质(第t步的概率只和第t-1步有关):
可以通过该模型,计算出第t步的“行情大涨”的概率吗?如果这个概率大于0.5那么输出1,否则输出0。
输入描述
第1行输入为测试数据组数T(1<=T<1000),接下来T组每5行的数据格式为
T组第1行是步长1<=t<=10。
T组第2行是一个3维的初始状态
T组第3行到第5行是3*3的概率转移矩阵P,每行有三个浮点数
输出描述
如果第t步的“行情大涨”概率大于0.5那么输出1,否则输出0
参考代码
HMM 矩阵乘法
CPP版本
#include <bits/stdc++.h>
using namespace std;
void solve() {
int t;
vector<double> a(3);
vector<vector<double>> p(3, vector<double>(3));
cin >> t;
for (int i = 0; i < 3; ++i) {
cin >> a[i];
}
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
cin >> p[i][j];
}
}
while (t--) {
auto tmp = a;
for (int i = 0; i < 3; ++i) {
a[i] = 0;
for (int j = 0; j < 3; ++j) {
a[i] += tmp[j] * p[i][j];
}
}
}
cout << (a[2] > 0.5 ? 1 : 0) << '\n';
}
int main() {
ios_base::sync_with_stdio(false), cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
solve();
}
return 0;
}
// 关注TechGuide! 大厂笔经面经闪电速递!
第三道:迎宾车队(100%)
题目描述
今天正在进行赛车车队选拔,每一辆赛车都有一个不可以改变的速度。现在需要选取速度差距在10以内的车队(车队中速度的最大值减去最小值不大于10),用于迎宾。车队的选拔按照的是人越多越好的原则,给出n辆车的速度,你能选出满足条件的最多车辆的车队吗。
输入描述
第一行一个数字n(1<=n<=100000)。
接下来一行n个整数,speed[i] 表示第i辆车的速度为speed(i)(1<=speed[i]<=109)。
输出描述
输出一行,最大车辆数目。
参考代码
#include <bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio(false), cin.tie(nullptr);
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; ++i) {
cin >> a[i];
}
sort(a.begin(), a.end());
int ans = 0;
for (int i = 0, j = 0; j < n; ++j) {
while (i < n && a[j] - a[i] > 10) {
i += 1;
}
ans = max(ans, j - i + 1);
}
cout << ans << '\n';
return 0;
}
// 关注TechGuide! 大厂笔经面经闪电速递!
第四道:水站的水流量(100%)
题目描述
有一个水站网络共有n层,第i层有i个水站节点,如图所示连接,水流单向流动。
每个水站在达到MAX个水流量时,称该水站满负荷工作,后续流进该水站的水流量,将会分为两等份流向它后面所连接的两个水站。若每秒都有流入第一个水站MAX个水流量,请问第T秒有多少个水站是满负荷工作的?
输入描述
一行两个正整数,n和t。
输出描述
一个正整数,第t秒满负荷工作的水站数量。
参考代码
模拟
#include <bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio(false), cin.tie(nullptr);
int n, t;
cin >> n >> t;
if (n == 1) {
cout << 1 << '\n';
return 0;
}
int N = n * (n + 1) / 2;
vector<int> a(N + 1);
int b = 1 << 10;
while (t--) {
a[1] += b;
int s = 1, d = 1;
while (s <= N - n - n + 2) {
for (int i = s; i < s + d; ++i) {
if (a[i] > b) {
int r = (a[i] - b) / 2;
a[i + d] += r;
a[i + d + 1] += r;
a[i] = b;
}
}
s += d;
d += 1;
}
}
int ans = 0;
for (int i = 1; i <= N; ++i) {
if (a[i] >= b) {
ans += 1;
}
}
cout << ans << '\n';
return 0;
}
// 关注TechGuide! 大厂笔经面经闪电速递!
第五道:定点轰炸(100%)
题目描述
牛牛是军团的指挥官,这一天,高层来到团中检查,牛牛为了演示军团的实战能力,决定表演定点轰炸。
在事先既定的 n*n大小的矩形中,随机勾勒上几笔,轰炸位置即为被这几笔所包围的区域。
你作为自动化定点轰炸的创始人,需要为此书写一个程序,来完成这个任务。首先,你将整个 n*n的矩形看成一个全 0矩形,勾勒的笔画所经过的位置用 1表示,现在,你需要将被轰炸区域改成数字 2,用于标记,指引轰炸方位。
一个被 1包围的区域定义为:区域内部的点不能通过上下左右的移动,在不经过 1的前提下到达区域外的 0点或者矩形外部。
输入描述
第一行输入一个正整数n(1<=n<1000) ,代表矩形边长。
接下去 n 行,每行 n个整数,整数仅有可能为 0或 1,含义如题所述。
输出描述
输出一个 n*n的矩形,一共 n行,每行 n 个整数,该矩形代表标记完轰炸区域后的结果呈现。
由于勾勒的随机性,有可能不能形成封闭区域,此时,只需要输出原矩形即可。
参考代码
首先状态数组i和j表示区间,很明显,0和1表示该区间全部变成哪边的数字,0设为区间最左边的数值,1为最右边。然后要了解这题dp的核心,即状态转移方程本状态与前某状态的关系,就是无论哪个区间变成数字都相同,最终一定转变成该区间两侧的某一个数值,所以每次区间定义两个表示全部转变成最左边或最右边数值时的最小花费,所以新区间最左值就是右侧最左的花费加上转移话费 和右侧区间最右的花费加上转移的花费 的最小值。右侧数值同理。
#include<bits/stdc++.h>
using namespace std;
int f(vector<int>& p){
int len = p.size();
vector<vector<vector<int>>> dp(len,vector<vector<int>>(len,vector<int>(2,0)));
for(int l = 2; l <= len; ++l){
for(int i = 0; i <= len-l; ++i){
int j = i+l-1;
dp[i][j][0] = min(dp[i+1][j][0]+abs(p[i]-p[i+1]),dp[i+1][j][1]+abs(p[i]-p[j]));
dp[i][j][1] = min(dp[i][j-1][1]+abs(p[j]-p[j-1]),dp[i][j-1][0]+abs(p[j]-p[i]));
}
}
return min(dp[0][len-1][0],dp[0][len-1][1]);
}
int main(){
vector<int> p({3,2,1,5});
cout<<f(p);
return 0;
}
// 关注TechGuide! 大厂笔经面经闪电速递!
恭喜发现宝藏!微信搜索公众号【TechGuide】关注更多新鲜好文和互联网大厂的笔经面经。