P5776 题解 & 图的耳分解

· · 题解

Solution

本题是无向图最小权耳分解板子题。略微记录一些耳分解相关内容。

定义无向图 G=(V,E) 对其导出子图 G'=(V',E') 存在耳,当前仅当可以找到一组顶点 (x_1,x_2,\dots,x_{t-1},x_t),其中 x_1,x_t \in V\{x_2,x_3,\dots,x_{t-1}\} = V / V',且 \forall 1 \le i \le t-1x_ix_{i+1} 有边,且除了 x_1x_t 外这组顶点两两不同。这样的一组顶点称为 GG' 的耳,如果 x_1 \neq x_t 称为 GG' 的开耳。

直观地看,就像 G' 外面挂了一个“耳朵”形成了 G。定义并不关注 V / V'V' 之间的连边情况,也就是完全图任何点集是他的一个耳。

对于有向图,也容易定义他的“耳”,只要确定边的方向一定即可。

定义无向图的耳分解为:

取无向图 G 的若干个导出子图 G_0=(V_0,E_0)G_1 = (V_1,E_1)\dotsG_t=(V_t,E_t)

其中 G_t=GV_0 \subset V_1 \subset \cdots \subset V_tG_0 存在哈密顿回路(就是能形成一个环),且 \forall 1 \le i \le tG_iG_{i-1} 的耳存在。

这样的耳构成的集合称为无向图的耳分解。

特别的,如果每个耳都是开耳,称为无向图的开耳分解。

同样容易定义有向图的耳分解。

无向图的耳分解实际上刻画了图的双连通性。

$\rm Theorem \ 2$:**无向图存在开耳分解当且仅当无向图点双联通**。 $\rm Theorem \ 3$:**有向图存在耳分解当且仅当有向图强联通**。 定理证明: 必要性证明逆否命题。比较显然:存在割边、割点、多个极大强联通分量,显然就找不到耳 充分性证明考虑构造性证明。以 $\rm Theorem \ 1$ 为例,我们求出原图的一棵 DFS 树。将包含根的任意一个环当做 $G_0$。在 $G$ 扩张为原图之前,必定能找到一个点 $u$ 不在 $G$ 中,它存在返祖边且另一端点在 $G$ 中。那么很容易将这个耳朵挂在原图上。由于不存在割边,这样的 $u$ 一定能找到。 ---------- 回到本题,我们本质上在求解无向图的最小权耳分解。 考虑使用**状压 DP**,层层增加耳。 设 $dp_{S,i,j}$ 为“将 $S$ 中的点放进耳分解中,目前存在一个不完整的耳并且最后一个节点是 $i$,这只耳最后要连到 $j$ 上”所需的最小代价即可。 复杂度 $O(n^32^n)$。 - Q:为什么要记录 $j$? - A:否则你无法区分哪些点属于当前耳,哪些点属于之前确定好的耳。 一个小小的细节:耳可能仅含有一对重边,也可能只包含一个外部点,这个特判。 代码: ```cpp #include<bits/stdc++.h> #define int long long #define ffor(i,a,b) for(int i=(a);i<=(b);i++) #define roff(i,a,b) for(int i=(a);i>=(b);i--) using namespace std; const int MAXN=20,MAXM=(1<<12)+10,INF=0x3f3f3f3f3f3f3f3f; int T,n,m,dp[MAXM][MAXN][MAXN],ans[MAXM]; vector<int> G[MAXN][MAXN]; int calc(int u,int v) { if(G[u][v].empty()) return INF; return G[u][v][0]; } signed main() { ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); cin>>T; while(T--) { cin>>n>>m; ffor(i,1,n) ffor(j,1,n) G[i][j].clear(); memset(dp,0x3f,sizeof(dp)),memset(ans,0x3f,sizeof(ans)); ffor(i,1,m) { int u,v,w; cin>>u>>v>>w; G[u][v].push_back(w),G[v][u].push_back(w); } ffor(u,1,n) ffor(v,1,n) { sort(G[u][v].begin(),G[u][v].end()); if(u<v) { if(G[u][v].size()>=2) ans[(1<<u-1)+(1<<v-1)]=G[u][v][0]+G[u][v][1]; dp[(1<<u-1)+(1<<v-1)][u][v]=dp[(1<<u-1)+(1<<v-1)][v][u]=calc(u,v); } } ffor(s,1,(1<<n)-1) { ffor(u,1,n) ffor(v,1,n) if(!(s&(1<<u-1))&&(s&(1<<v-1))) ffor(w,1,n) if(s&(1<<w-1)) dp[s|(1<<u-1)][u][w]=min(dp[s|(1<<u-1)][u][w],ans[s]+calc(u,v)); ffor(u,1,n) ffor(v,1,n) if((s&(1<<u-1))&&(s&(1<<v-1))&&u!=v) ffor(k,1,n) if(!(s&(1<<k-1))) { dp[s|(1<<k-1)][k][v]=min(dp[s|(1<<k-1)][k][v],dp[s][u][v]+calc(k,u)); if(G[u][k].size()&&G[k][v].size()) ans[s|(1<<k-1)]=min(ans[s|(1<<k-1)],min(ans[s],dp[s][u][v])+calc(k,u)+calc(k,v)); } ffor(u,1,n) ffor(v,1,n) if((s&(1<<u-1))&&!(s&(1<<v-1))) if(G[u][v].size()>=2) ans[s|(1<<v-1)]=min(ans[s|(1<<v-1)],ans[s]+G[u][v][0]+G[u][v][1]); } if(ans[(1<<n)-1]<INF) cout<<ans[(1<<n)-1]<<'\n'; else cout<<"impossible\n"; } return 0; } ```