AT2657 [ARC078D] Mole and Abandoned Mine 题解
题目链接:AT2657 [ARC078D] Mole and Abandoned Mine
题意:
cxy 准备住在一个废弃的矿洞里,矿洞的结构可以用一个包含 $n$ 个点,$m$ 条边的连通无向简单图来表示,其中点以 $1,2,\cdots,n$ 编号。第 $i$ 条边连接点 $a_i$ 和 $b_i$,并且移除它需要花费 $c_i$ 元。cxy 想要移除一些边,使得点 $1$ 和点 $n$ 之间有且仅有唯一的路径,请你帮她寻找满足要求所需的最小花费。
注:一条路径 (path) 指的是不能经过重复点的途径 (walk),一条迹 (trail) 指的是不能经过重复边的途径。
输入格式:
n m a1 b1 c1 ⋮ ⋮ ⋮ am bm cm
输出格式:
一行,即答案。
数据范围:
保证是连通简单图。
$2 \leq n \leq 15,~n - 1 \leq m \leq \dbinom n2,~1 \leq a_i, b_i \leq n ,~1 \leq c_i \leq 10^6$
它要求 $1$ 到 $n$ 只有 $1$ 条路径,并且不用经过所有点
因此这张图应该长成这样
也就是每个保留路径上的结点都挂着一些结点。
删除的边权值和最小并不是很好处理,可以反过来计算保留的边权值和最大。
注意到 $n\le 15$ ,考虑状压dp
设 $f_{A,u}~(u \in A)$ 表示可以到达的点集为 $A$ ,主路上现在走到 $u$ ,保留的边权值和的最大值。
结合下图,其中蓝色部分表示 $f_{A,u}$ 。
考虑转移。
下文中使用 $a\uparrow b+c$ 表示 $a \leftarrow \max\{a,b+c\}$
第一种,在主路上加入与 $u$ 连通的 $v$ ,也就是图中的红色部分,则
第二种,在 $u$ 下面挂一个连通块,如果把上面的 $v$ 看成 $u$ ,那连通块就是绿色部分,则
其中,
$\mathtt{out}(u,B)$ 表示 $B$ 中所有与 $u$ 直接相连的点的边权和,即
$\mathtt{in}(B)$ 表示原图 $G$ 的导出子图 $G[B]$ 中所有点的边权和,即
$\mathtt{in},\mathtt{out}$ 都是可以在 $O(2^nn^2)$ 的时间内处理出来的
dp转移的复杂度为 $O(3^n n)$
总时间复杂度 $O(2^nn^2 + 3^nn)$ ,看上去后者是瓶颈。
代码:(不明白的话可以参考DP总结-状压dp)
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdarg>
#include <cmath>
#include <iomanip>
#include <random>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N ((int)(20))
#define M ((1<<15)+15)
int n,m,res,g[N][N];
int in[M], out[N][M], f[M][N];
void up(int &x,int y) { x < y ? x = y : 0;}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
cin >> n >> m;
for(int i=0,u,v,w; i<m; i++)
{
cin >> u >> v >> w; --u; --v;
g[u][v] = g[v][u] = w; res+=w;
}
int mx = 1 << n;
// proc in[]
for(int i=0; i<mx; i++)
for(int u=0; u<n; u++) if((i >> u) & 1)
for(int v=u+1; v<n; v++) if((i >> v) & 1)
in[i] += g[u][v];
// proc out[][]
for(int i=0; i<mx; i++)
for(int u=0; u<n; u++) if(!((i >> u) & 1))
for(int v=0; v<n; v++) if((i >> v) & 1)
out[u][i] += g[u][v];
memset(f,0xc0,sizeof(f));
f[1][0] = 0;
for(int i=1; i<mx; i++)
for(int u=0; u<n; u++)
if(((i >> u) & 1) && f[i][u] >= 0)
{
for(int v=0; v<n; v++)
if(!((i >> v) & 1) && g[u][v])
up(f[i | (1 << v)][v], f[i][u] + g[u][v]);
int j = ~ ( ((-1) << n) | i );
for(; j; j=(j-1) & (~i))
up(f[i | j][u], f[i][u] + out[u][j] + in[j]);
}
cout << res-f[(1 << n) - 1][n-1] << '\n';
return 0;
}
参考文献:
[1] https://yhx-12243.github.io/OI-transit/records/ac2657%3Barc078F.html