把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷5776】[SNOI2013] Quare(状压DP)

点此看题面

  • 给定一张\(n\)个点\(m\)条边的图,求边权和最小的边双子图。
  • \(n\le12,m\le40\)

边双的拆解

边双相关的一个结论:一个边双可以拆成一个边双子图以及一条链。

因此我们设\(f_i\)表示使得点集\(i\)成为边双的最小边权和,\(g_{x,y,i}\)表示以\(x,y\)为两端、由点集\(i\)中的点构成的链的最小边权和,并设\(Mn_{x,i}/Sn_{x,i}\)表示\(x\)到点集\(i\)的最小/次小边权辅助转移。

记录两点\(x,y\)间的最小边权为\(w_{x,y}\),次小边权为\(u_{x,y}\)

\(Mn_{x,i}/Sn_{x,i}\) 的转移显然是非常简单的,就是枚举点集中的一个点 \(y\),将 \(w_{x,y}\)\(u_{x,y}\) 与当前最小值和次小值比较。

\(g_{x,y,i}\)的转移只需考虑枚举\(i\)以外的一个点\(z\)扩展,加上\(w_{y,z}\),将\(z\)加入点集中并更新端点\(y\)\(z\)

\(f_i\)的转移就是考虑枚举\(i\)以外的一个点集\(j\),在其中枚举两个端点\(x,y\),列出转移式:

\[f_{i\cup j}=\begin{cases}f_i+g_{x,y,j}+Mn_{x,i}+Mn_{y,i}&x\not=y,\\f_i+g_{x,y,j}+Mn_{x,i}+Sn_{y,i}&x=y\end{cases} \]

代码:\(O(3^nn^2)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 12
#define INF (int)7e6
#define Gmin(x,y) (x>(y)&&(x=(y)))
using namespace std;
int n,m,w[N+5][N+5],u[N+5][N+5],f[1<<N],g[N+1][N+1][1<<N],Mn[N+1][1<<N],Sn[N+1][1<<N];
int main()
{
	RI Tt,i,j,k,l,x,y,z;scanf("%d",&Tt);W(Tt--)
	{
		for(scanf("%d%d",&n,&m),i=1;i<=n;++i) for(j=1;j<=n;++j) w[i][j]=u[i][j]=i^j?INF:0;
		for(i=1;i<=m;++i) scanf("%d%d%d",&x,&y,&z),
			w[x][y]>z?(u[x][y]=u[y][x]=w[x][y],w[x][y]=w[y][x]=z):u[x][y]=u[y][x]=min(u[x][y],z);//求出两点间最小边权和次小边权
		for(l=1<<n,i=1;i^l;++i) f[i]=INF;for(i=1;i<=n;++i) for(j=1;j<=n;++j) for(k=1;k^l;++k) g[i][j][k]=INF;//初始全赋成INF
		for(i=1;i<=n;++i) f[1<<i-1]=g[i][i][1<<i-1]=0;//单点赋成0
		for(i=1;i<=n;++i) for(j=1;j^l;++j) for(Mn[i][j]=Sn[i][j]=INF,x=1;x<=n;++x)//预处理点到点集的最小边权/次小边权
			(j>>x-1&1)&&(Mn[i][j]>w[i][x]?(Sn[i][j]=min(Mn[i][j],u[i][x]),Mn[i][j]=w[i][x]):Gmin(Mn[i][j],w[i][x]));//枚举点集中每个点比较
		for(k=1;k^l;++k) for(i=1;i<=n;++i) for(j=1;j<=n;++j)//预处理g
			for(x=1;x<=n;++x) !(k>>x-1&1)&&Gmin(g[i][x][k|(1<<x-1)],g[i][j][k]+w[j][x]);//枚举一个新端点扩展
		for(i=1;i^l;++i) for(j=1;j^l;++j) if(!(i&j)) for(x=1;x<=n;++x) for(y=1;y<=n;++y)//DP求解f
			(j>>x-1&1)&&(j>>y-1&1)&&Gmin(f[i|j],f[i]+g[x][y][j]+Mn[x][i]+(x^y?Mn:Sn)[y][i]);//根据x,y是否相同简单讨论
		f[l-1]==INF?puts("impossible"):printf("%d\n",f[l-1]);
	}return 0;
}
posted @ 2021-05-25 20:46  TheLostWeak  阅读(37)  评论(0编辑  收藏  举报