题解:P5776 [SNOI2013] Quare
Lovya
·
2026-03-08 23:32:45
·
题解
:::info[简要题意]{open}
给定一个 n 个点,m 条边的带权无向图,寻找一个边双连通子图,使其边权和最小。可能有重边。
::::
:::info[耳与耳分解的定义]{open}
假如图 G 有一个真子图 G^{\prime} ,点 a 为 G^{\prime} 上一点。点 a 与 G^{\prime} 外一点相连。假如此点有另一条不经过点 a 的路径连到 G^{\prime} ,设这条路径连到 G^{\prime} 内的点 b 。称点 a 到点 b 的 G^{\prime} 外路径为耳。若一个图 G 可以拆为一个环(即简单回路)与多个耳,则称图 G 可以耳分解。
:::
::::info[结论:图 G 边双连通 \Lrarr 图 G 存在耳分解]{open}
:::info[必要性证明:若图 G 存在耳分解,则图 G 边双连通]{open}
由于图 G 存在耳分解,则图 G 必含环。任取一环作为 G_0 。当 G_0=G 时,证明完成。否则,设已证得 G 的真子图 G_{i-1} 边双连通,考虑 G_i = G_{i-1} \cup P_i ,其中 P_i 是一个耳,P_i 的两端点为 a,b 。下证 G_i 边双连通。
由于 G_{i-1} 连通,且点 a,b 在 G_{i-1} 中,故 G_i 连通。
反证:假设 G_i 有割边 e=(u,v) 。
分类讨论:
删去 e 后,有路径 u\to a\to b\to v ,得到一条连接 u 与 v 的路径,故 e 非割边。
显然,因 G_{i-1} 边双连通,因此, G_{i-1} 中无割边。故,e 非割边。
上述两种情况均矛盾,故 G_i 无割边,即 G_i 边双连通。归纳可得,G_k = G 边双连通。
:::
:::info[充分性证明:若图 G 边双连通,则图 G 存在耳分解]{open}
由于图 G 边双连通,图 G 必含环。任取一环作为 G_0 。当 G_0=G 时,分解完成。否则,设已构造出 G 的真子图 G_i ,且 G_i 满足可由 G_0 逐步添加耳得到。
由于 G_i 为 G 的真子图,故有 G_i\not=G 。因此,必存在一条边 (u,v) ,满足 u \in G_i,v \not \in G_i 。根据边双连通的定义,删去边 (u,v) 后必存在另一条路径使得 v 与 G_i 连通。取删去边 (u,v) 后的图 G 中 v 到 G_i 的最短路径为 P ,设其终点为 w 。令 P'=(u,v)\cup P ,则 P' 为 G_i 的一个耳。将 P' 加入 G_i 得 G_{i+1}=G_i\cup P' 。重复此过程,每次至少增加一条新边,有限步后得 G ,即得耳分解。
:::
::::
$g_{S,i,j}$ 表示当前已考虑的点为 $S$,但是 $S$ 中最后加入的一个耳还未完成的,这个耳以 $i$ 为起点,以 $j$ 为终点,此时已选择的边的最短长度。
$val_{i,j,0/1}$ 表示 $i$ 到 $j$ 的所有边中的最小/次小边权。
1. 开始一个新的耳
从边双连通子图 $S$ 开始,选择 $S$ 中的点 $j$ 和新点 $i$,用边 $(i,j)$ 连接,并计划将耳的另一个端点设为 $S$ 中的点 $k$。这相当于开始构建一个耳,目前只有点 $i$,路径从 $i$ 到 $k$ 的部分尚未完成。注意是开始构建一个耳,这也是和下面扩展一个耳的区别。

$$g_{S\cup \{i\},i,k}=\min_{
\begin{subarray}{}
i\not \in S\\
j\in S\\
k\in S
\end{subarray}
} f_S+val_{i,j,0}$$
2. 扩展一个耳
在耳的起点 $i$ 处添加新点 $k$,用边 $(i, k)$ 连接,耳扩展后剩下从 $k$ 到 $j$ 的部分未完成。

$$g_{S\cup \{k\},k,j}=\min_{
\begin{subarray}{}
i\in S\\
j\in S\\
k\not \in S\\
i\not=j
\end{subarray}
} g_{S,i,j}+val_{i,k,0}$$
3. 闭合一个耳
添加新点 $k$,并用两条边分别连接 $k$ 到路径的两个端点 $i$ 和 $j$,形成一个环。此时点集 $S\cup\{k\}$ 成为边双连通子图。

$$f_{S\cup \{k\}}=\min_{
\begin{subarray}{}
i\in S\\
j\in S\\
k\not \in S\\
i\not=j
\end{subarray}
} \min(f_S,g_{S,i,j})+val_{i,k,0}+val_{j,k,0}$$
4. 添加单点闭耳(二元环)
从边双连通子图 $S$ 添加新点 $i$,并用两条边(最小和次小)连接 $i$ 到 $S$ 中的点 $j$,形成一个以 $j$ 为端点的二元环。

$$f_{S\cup \{i\}}=\min_{
\begin{subarray}{}
i\not \in S\\
j\in S
\end{subarray}
} f_S+val_{i,j,0}+val_{i,j,1}$$
总的时间复杂度是 $\Omicron(n^32^n)$ 的。
:::info[代码]{open}
```cpp
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int T,n,m,x,y,z,w_val[15][15][2],f[20005],g[20005][15][15];
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d %d",&n,&m);
memset(w_val,0x3f,sizeof(w_val));
memset(f,0x3f,sizeof(f));
memset(g,0x3f,sizeof(g));
for(int i=1;i<=m;i++)
{
scanf("%d %d %d",&x,&y,&z);
if(w_val[x][y][0]>z)
{
w_val[x][y][1]=w_val[x][y][0];
w_val[x][y][0]=z;
}
else if(w_val[x][y][1]>z)
w_val[x][y][1]=z;
w_val[y][x][0]=w_val[x][y][0];
w_val[y][x][1]=w_val[x][y][1];
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i>=j)
continue;
if(w_val[i][j][0]<0x3f3f3f3f&&w_val[i][j][1]<0x3f3f3f3f)
f[(1<<(i-1))+(1<<(j-1))]=w_val[i][j][0]+w_val[i][j][1];
g[(1<<(i-1))+(1<<(j-1))][i][j]=w_val[i][j][0];
}
}
for(int s=1;s<(1<<n);s++)
{
for(int i=1;i<=n;i++)
{
if(s&(1<<(i-1)))
continue;
for(int j=1;j<=n;j++)
{
if(!(s&(1<<(j-1))))
continue;
for(int k=1;k<=n;k++)
{
if(!(s&(1<<(k-1))))
continue;
g[s|(1<<(i-1))][i][k]=min(g[s|(1<<(i-1))][i][k],f[s]+w_val[i][j][0]);
}
}
}
for(int i=1;i<=n;i++)
{
if(!(s&(1<<(i-1))))
continue;
for(int j=1;j<=n;j++)
{
if(!(s&(1<<(j-1))))
continue;
if(i==j)
continue;
for(int k=1;k<=n;k++)
{
if(s&(1<<(k-1)))
continue;
g[s|(1<<(k-1))][k][j]=min(g[s|(1<<(k-1))][k][j],g[s][i][j]+w_val[i][k][0]);
if(w_val[i][k][0]<0x3f3f3f3f&&w_val[j][k][0]<0x3f3f3f3f)
f[s|(1<<(k-1))]=min(f[s|(1<<(k-1))],min(f[s],g[s][i][j])+w_val[i][k][0]+w_val[j][k][0]);
}
}
}
for(int i=1;i<=n;i++)
{
if(s&(1<<(i-1)))
continue;
for(int j=1;j<=n;j++)
{
if(!(s&(1<<(j-1))))
continue;
if(w_val[i][j][0]>=0x3f3f3f3f||w_val[i][j][1]>=0x3f3f3f3f)
continue;
f[s|(1<<(i-1))]=min(f[s|(1<<(i-1))],f[s]+w_val[i][j][0]+w_val[i][j][1]);
}
}
}
if(f[(1<<n)-1]>=0x3f3f3f3f)
printf("impossible\n");
else
printf("%d\n",f[(1<<n)-1]);
}
return 0;
}
```