2023大厂真题提交网址(含题解):
www.CodeFun2000.com(http://101.43.147.120/)
最近我们一直在将收集到的机试真题制作数据并搬运到自己的OJ上,供大家免费练习,体会真题难度。现在OJ已录入50+道2023年最新大厂真题,同时在不断的更新。同时,可以关注"塔子哥学算法"公众号获得每道题的题解。
题目1:[HNOI2013]游走
题目大意
给你一张无向连通图,从第 1 1 1点随机游走到 n n n点。花费为经过的边的编号的和.问你如何安排边的编号使得期望花费最小化.
n ≤ 500 , m ≤ n 2 n \leq 500,m \leq n^2 n≤500,m≤n2
题目思路:
很容易想到,求出每条边期望经过次数,然后贪心安放即可.
但是 m m m太大,无法求解.考虑先求每个点期望经过次数.
考虑到
n
n
n点就停止了,所以不考虑
n
n
n的贡献。
显然有转移:
f
1
=
1
+
∑
(
1
,
j
)
∈
E
f
j
d
u
j
f_1=1+\sum_{(1,j)\in E}^{}\frac{f_j}{du_j}
f1=1+∑(1,j)∈Edujfj
f
n
=
0
f_n=0
fn=0
f
i
=
∑
(
i
,
j
)
∈
E
f
j
d
u
j
,
i
∈
[
2
,
n
)
f_i=\sum_{(i,j)\in E}^{}\frac{f_j}{du_j}, \ i \in [2,n)
fi=∑(i,j)∈Edujfj, i∈[2,n)
对于一条边 ( i , j ) (i,j) (i,j),它期望经过的次数是:
g i , j = f i d u i + f j d u j g_{i,j}=\frac{f_i}{du_i} + \frac{f_j}{du_j} gi,j=duifi+dujfj
所以跑高斯消元后,对边排序计算即可。
AC代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pb push_back
#define pii pair<int,int>
#define mp make_pair
const int maxn = 505;
const double eps = 1e-8;
vector<int> e[maxn];
int du[maxn];
double a[maxn][maxn];
double res[maxn] , ans[maxn * maxn];
int main()
{
ios::sync_with_stdio(false);
int n , m; cin >> n >> m;
for (int i = 1 ; i <= m ; i++){
int x , y; cin >> x >> y;
e[x].pb(y);
e[y].pb(x);
du[x]++;
du[y]++;
}
for (int i = 1 ; i <= n ; i++) a[i][i] = 1;
for (int i = 1 ; i < n ; i++){
for (auto v : e[i]){
a[i][v] = -1.0 / du[v];
}
}
a[1][n + 1] = 1;
// guess消元
int r , c;
for (r = c = 1 ; r <= n && c <= n ; r++ , c++){
int p = r;
for (int j = r + 1 ; j <= n ; j++){
if (a[p][c] < a[j][c]){
p = j;
break;
}
}
// 不会有自由元
swap(a[p] , a[r]);
for (int j = 1 ; j <= n ; j++){
if (j == r) continue;
if (fabs(a[r][c]) < eps) continue;
double d = a[j][c] / a[r][c];
for (int k = c ; k <= n + 1 ; k++)
a[j][k] -= d * a[r][k];
}
}
for (int i = 1 ; i <= n ; i++) res[i] = a[i][n + 1] / a[i][i];
int cnt = 0;
for (int i = 1 ; i <= n ; i++){
for (auto v : e[i]){
if (v < i) continue;
ans[++cnt] = res[i] / du[i] + res[v] / du[v];
}
}
sort(ans + 1 , ans + 1 + cnt);
double gg = 0;
// cout << "cnt = " << cnt << endl;
for (int i = 1 ; i <= cnt ; i++){
gg += (cnt - i + 1.0) * ans[i];
}
cout << fixed << setprecision(3);
cout << gg << endl;
return 0;
}
题目2:bzoj3270-博物馆
题目大意:
给出一个无向图,两个人初始在两个点上。当一个人在一个点i上的时候,每一次,他有 p [ i ] p[i] p[i]的概率留在原位,有 1 − p [ i ] 1−p[i] 1−p[i]的概率等概率地选择直接连边的一个点走出去。当两个人在同一时刻走到同一个点,那么他们相遇,过程结束。现在求他们在每一个点相遇的概率。
题目思路:
跟题目1没本质区别,就是升维了。令 f ( i , j ) f(i,j) f(i,j)代表两个人从最开始到站在 i , j i,j i,j期望次数,然后大力转移即可.
题目3:2019HNCPC-H-有向图
题目大意:
给你一张图。 n + m n+m n+m个点。从1点开始随机游走。有一个概率矩阵 P i , j P_{i,j} Pi,j代表从 i 到 j i到j i到j的概率.后 m m m个点走到后则只会原地踏步.问你无限次之后停在后m个点的概率.
题目思路:
令 E ( i ) E(i) E(i)为无限次之后经过 i i i点的期望次数。由于到 i , i ∈ [ n + 1 , n + m ] i ,\ i \in[n+1,n +m] i, i∈[n+1,n+m]后就会停止。所以求解出来的期望就是概率。
转移和上述问题无差别,列方程高斯消元即可.
题目4:HDU5955-AC自动机+概率dp+高斯消元
题目大意:
n个人,每个人有一个长度为 L L L的字符串。你随机生成字符串无限次直到最后 L L L个字符串为某个人的字符串.问每个人的概率.
题目思路:
建Tire图。在Tire图上跑概率dp.然后高斯消元即可.
问题结构和题目3一模一样。我们可以直接求期望,然后概率=期望.