2023大厂真题提交网址(含题解):
www.CodeFun2000.com(http://101.43.147.120/)
最近我们一直在将收集到的机试真题制作数据并搬运到自己的OJ上,供大家免费练习,体会真题难度。现在OJ已录入50+道2023年最新大厂真题,同时在不断的更新。同时,可以关注"塔子哥学算法"公众号获得每道题的题解。
问题前言以及历史:转OI WIKI
问题定义:
给你一张图,问你必须连通 k k k个关键点的最小边权和花费.
n , m ≤ 100 , k ≤ 10 n,m \leq 100 , k \leq 10 n,m≤100,k≤10
最小生成树是最小斯坦纳树的一种特殊情况. k = n k=n k=n.
问题解决:
使用状态压缩dp解决此问题。这之前,需要知道一个结论:答案一定是一棵树.
反证:假设有环,我们可以显然可以破环而不破环连通性。所以结果一定是树。
那么令: d p ( i , s ) dp(i,s) dp(i,s)代表以 i i i为这棵树的根,且集合 s s s的点已经被这棵树连通的最小代价。
转移方程:
第一种情况:由它的儿子整个转移给他的: d p ( s , j ) + w ( j , i ) → d p ( s , i ) dp(s,j)+w(j,i) \rightarrow dp(s,i) dp(s,j)+w(j,i)→dp(s,i)
如下图关键点
k
1
k_1
k1转移到
i
i
i点就是第一种情况.
第二种情况:由它的两个不同的儿子的不同集合取并而成,过程类似背包:
d
p
(
s
−
t
,
i
)
+
d
p
(
t
,
i
)
→
d
p
(
s
,
i
)
dp(s-t,i)+dp(t,i) \rightarrow dp(s,i)
dp(s−t,i)+dp(t,i)→dp(s,i)
如下图关键点
k
1
,
k
2
k_1,k_2
k1,k2转移到
i
i
i点就是第二种情况.
对于第一种转移,将
s
s
s公共因子提出来发现就是一个最短路的转移方程,所以直接跑dijstra或者spfa。
对于第二种转移,我们枚举子集的子集就好了。
基本模板(洛谷P6192):
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
#define vi vector<int>
const int maxn = 105;
const int inf = 0x3f3f3f3f;
int dp[maxn][1 << 10];
vector<pii> e[maxn];
int p[12];
priority_queue<pii> q;
int bk[maxn];
void dijstra (int s)
{
memset(bk , 0 , sizeof bk);
while(q.size()){
pii g = q.top();q.pop();
int id = g.second;
if (bk[id]) continue;
bk[id] = 1;
for (auto d : e[id]){
int v = d.first , w = d.second;
if (dp[v][s] > dp[id][s] + w)
dp[v][s] = dp[id][s] + w , q.push(mp(-dp[v][s] , v));
}
}
return ;
}
int main()
{
ios::sync_with_stdio(false);
int n , m , key; cin >> n >> m >> key;
for (int i = 1 ; i <= m ; i++){
int x , y , z;
cin >> x >> y >> z;
e[x].pb (mp(y , z));
e[y].pb (mp(x , z));
}
memset(dp , 0x3f , sizeof dp);
for (int i = 1 ; i <= key ; i++){
cin >> p[i];
dp[p[i]][1 << (i-1)] = 0;
}
for (int s = 1 ; s < (1 << key) ; s++){
for (int i = 1 ; i <= n ; i++){
for (int ss = (s - 1)&s ; ss ; ss = (ss - 1)&s)
dp[i][s] = min(dp[i][s] , dp[i][ss] + dp[i][s - ss]);
if (dp[i][s] != inf) q.push(mp(-dp[i][s] , i));
}
dijstra(s);
}
cout << dp[p[1]][(1 << key) - 1] << endl;
return 0;
}