BZOJ4883 棋盘上的守卫 基环树、Kruskal

题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=4883

题意:给出一个$N \times M$的棋盘,每个格子有权值。你需要每一行选中一个格子,每一列也选中一个格子(一个格子不能同时被行选中和被列选中),求这些格子权值和的最小值,$2 \leq N,M \leq 10^5 , N \times M \leq 10^5$


考虑将行与列拆成点,格子的权值变为连接其对应行与列对应节点的边,我们的问题也就是需要找到一个边集,使得每一条边都在只匹配其端点中的一个的情况下匹配到每一个点,并且边权之和最小。考虑如何找“每一条边都在只匹配其端点中的一个的情况下匹配到每一个点”的边集。我们发现一棵有$N$个节点的树可以匹配$N - 1$个点,那么我们再在树上加一条边,构成基环树,就能够满足条件了。如果我们将边变为有向边,方向向其匹配的那个点,那么满足条件的基环树就是基环外向树。所以我们的目标就是找到这个图中的最小生成基环森林,使用类似$Kruskal$的方法可以实现。

具体的实现方式在并查集上有不同。我们维护某个集合中是否有环。如果某条边对应的两个端点在同一并查集中,如果没有环则设为有环并加上边权,否则无法合并;合并时两个有环的并查集无法合并,否则合并这两个集合,并且继承有无环的状态。

 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 #define MAXN 100010
 4 using namespace std;
 5 
 6 inline ll read(){
 7     ll a = 0;
 8     char c = getchar();
 9     while(!isdigit(c))
10         c = getchar();
11     while(isdigit(c)){
12         a = (a << 3) + (a << 1) + (c ^ '0');
13         c = getchar();
14     }
15     return a;
16 }
17 
18 struct Edge{
19     ll start , end , w;
20 }Ed[MAXN];
21 ll fa[MAXN] , N , M;
22 bool vis[MAXN];
23 
24 bool cmp(Edge a , Edge b){
25     return a.w < b.w;
26 }
27 
28 ll find(ll x){
29     return fa[x] == x ? x : (fa[x] = find(fa[x]));
30 }
31 
32 int main(){
33     ll ans = 0;
34     N = read();
35     M = read();
36     for(ll i = 1 ; i <= N ; i++)
37         for(ll j = 1 ; j <= M ; j++){
38             Ed[(i - 1) * M + j].start = i;
39             Ed[(i - 1) * M + j].end = j + N;
40             Ed[(i - 1) * M + j].w = read();
41         }
42     sort(Ed + 1 , Ed + N * M + 1 , cmp);
43     for(ll i = 1 ; i <= N + M ; i++){
44         fa[i] = i;
45         vis[i] = 0;
46     }
47     for(ll i = 1 ; i <= N * M ; i++){
48         ll p = find(Ed[i].start) , q = find(Ed[i].end);
49         if(p != q && !(vis[p] && vis[q])){
50             fa[q] = p;
51             ans += Ed[i].w;
52             vis[p] |= vis[q];
53         }
54         else
55             if(!vis[p]){
56                 vis[p] = 1;
57                 ans += Ed[i].w;
58             }
59     }
60     cout << ans;
61     return 0;
62 }

猜你喜欢

转载自www.cnblogs.com/Itst/p/9773824.html