Description

题目链接

求一张无向带权图的边双连通生成子图的最小代价。

Solution

核心的思路是,一个点双连通分量肯定是一堆环的并。

考虑增量地构造这个边双连通图,每次把一个环并进去,相当于加入了一条链。

那么这个转移需要:原集合的代价,链的代价,链的端点连入集合的代价。

设 \(A\) 为新图点集,\(S\) 为原图点集,设 \(f[S]\) 表示点集 \(S\) 构成边双连通分量的最小代价。

设 \(T\) 为新加入链的点集,\(u,v\) 分别为加入的链的端点,设 \(g[u][v][T]\) 表示该链的最小代价。

设 \(mm[u][S]\) 表示点 \(u\) 向集合 \(S\) 中的点所连边中,边权最小值。

\[f[A]=f[S]+g[u][v][T]+mn[u][S]+mn[v][S]
\]

但是注意,如果新加入的链退化成了一个点,加入的代价就算少了。

因此设 \(sec[u][S]\) 表示点 \(u\) 向集合 \(S\) 中的点所连边中,边权次小值。

那么对于 \(u=v\) 的情况:

\[f[A]=f[S]+g[u][u][T]+mn[u][S]+sec[u][S]
\]

预处理 \(mn\) 和 \(sec\) 复杂度 \(\mathcal O(n^2\times 2^n)\)

预处理 \(g\) 暴力枚举一个端点的变化,复杂度 \(\mathcal O(n^3\times 2^n)\)

计算 \(f\) 需要枚举子集,然后枚举 \(u, v\) ,复杂度 \(\mathcal O(n^2\times 3^n )\)

#include <cmath>
#include <cstdio>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 15
#define M 105
#define S 4105
using namespace std; inline int rd() {
int x = 0;
char c = getchar();
while (!isdigit(c)) c = getchar();
while (isdigit(c)) {
x = x * 10 + (c ^ 48); c = getchar();
}
return x;
} int n, m, tot, lim, hd[N]; struct edge{int w, to, nxt;} e[M << 1]; inline void add(int u, int v, int w) {
e[++tot].to = v; e[tot].w = w;
e[tot].nxt = hd[u]; hd[u] = tot;
} //mn[i][S]: i 到 S 最短路
//sec[i][S]: i 到 S 次短路
//g[i][j][S]: 一条链,节点集合为 S, 端点分别为 i, j
//f[S]: 集合为 S 的合法方案 int f[S], g[N][N][S], mn[N][S], sec[N][S]; inline void mmin(int &x, int y) {x = min(x, y);} inline int countbit(int s) {
int res = 0;
for (int i = 0; i < n; ++i)
res += ((s & (1 << i)) > 0);
return res;
} inline void work() {
n = rd(); m = rd();
tot = 0; lim = (1 << n);
for (int i = 0; i <= n; ++i) hd[i] = 0;
for (int i = 1, u, v, w; i <= m; ++i) {
u = rd() - 1; v = rd() - 1; w = rd();
add(u, v, w); add(v, u, w);
}
memset(f, 0x1f, sizeof(f));
memset(g, 0x1f, sizeof(g));
memset(mn, 0x1f, sizeof(mn));
memset(sec, 0x1f, sizeof(sec));
int inf = f[0];
//处理 mn 和 sec
for (int s = 1; s < lim; ++s)
for (int u = 0; u < n; ++u)
if ((s & (1 << u)) == 0)
for (int i = hd[u], v; i; i = e[i].nxt) {
v = e[i].to;
if ((s & (1 << v)) == 0) continue;
if (e[i].w < mn[u][s]) {
sec[u][s] = mn[u][s];
mn[u][s] = e[i].w; continue;
} else sec[u][s] = min(sec[u][s], e[i].w);
}
//处理 g
for (int u = 0; u < n; ++u) g[u][u][1 << u] = 0;
for (int s = 1; s < lim; ++s)
for (int u = 0; u < n; ++u)
for (int x = 0; x < n; ++x)
if (g[u][x][s] < inf)
for (int i = hd[u], v; i; i = e[i].nxt) {
v = e[i].to;
if (s & (1 << v)) continue;
mmin(g[v][x][s | (1 << v)], g[u][x][s] + e[i].w);
}
//处理 f
for (int u = 0; u < n; ++u) f[1 << u] = 0;
for (int nw = 1; nw < lim; ++nw)
if (countbit(nw) >= 2) {
for (int s = nw & (nw - 1); s; s = (s - 1) & nw) {
int t = nw - s;
for (int u = 0; u < n; ++u)
if (s & (1 << u)) for (int v = 0; v < n; ++v)
if (s & (1 << v) && g[u][v][s] < inf) {
if (u == v) f[nw] = min(f[nw], f[t] + g[u][v][s] + mn[u][t] + sec[u][t]);
else f[nw] = min(f[nw], f[t] + g[u][v][s] + mn[u][t] + mn[v][t]);
}
}
}
if (f[lim - 1] == inf) puts("impossible");
else printf("%d\n", f[lim - 1]);
} int main() {
int testcase = rd();
while (testcase--) work();
return 0;
}

最新文章

  1. tomcat6类加载器与类加载顺序
  2. 26. Remove Duplicates from Sorted Array
  3. EffectiveJava——用函数对象表示策略
  4. php中magic_quotes_gpc对unserialize的影响
  5. [转]Ubuntu 12.04 安装屏保
  6. Java 下 SSL 通信原理及实例
  7. C# List
  8. 【android】android中activity的启动模式
  9. HTML DOM 属性记录
  10. 小猪猪逆袭成博士之C++基础篇(三)字符串
  11. Jquery 时间格式化
  12. Fortran调用C语言小计
  13. 作为程序员你不知道中国互联网300强你就OUT了!
  14. LNMP 支持路由重写
  15. 2017-12-15python全栈9期第二天第五节之格式化输出补充之想要在格式化输出中表示单纯的%号就加%
  16. Dot &amp; cross product
  17. ref实现输入框聚焦
  18. delphi版本对应
  19. 029 RDD Join相关API,以及程序
  20. linux下查找nginx里的nginx.conf文件地址方法

热门文章

  1. Hadoop一些要注意的点
  2. KbmMW资源汇总(特别是xalion的文章)
  3. HDU1565 方格取数(1) —— 状压DP or 插头DP(轮廓线更新) or 二分图点带权最大独立集(最小割最大流)
  4. 织梦dedecms内页分类频道友情链接实现方法
  5. 【hdu 4374】One Hundred Layer
  6. 【AHOI2009】中国象棋
  7. vue项目中的路径别名
  8. Vue中devtools安装使用
  9. 出现&quot;Unable to instantiate Action,xxxxx, defined for &#39;login&#39; in namespace &#39;/&#39; xxxxx 解决办法
  10. Streamline Your App with Design Patterns 用设计模式精简你的应用程序