嘘~ 正在从服务器偷取页面 . . .

AT2657 [ARC078D] Mole and Abandoned Mine 题解


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\) ,也就是图中的红色部分,则 \[ f_{A\cup \{v\},v} \uparrow f_{A,u} + w(u,v) \] 第二种,在 \(u\) 下面挂一个连通块,如果把上面的 \(v\) 看成 \(u\) ,那连通块就是绿色部分,则 \[ f_{A \cup B,u} \uparrow f_{A,u} + \mathtt{out}(u,B) + \mathtt{in}(B) \] 其中,

\(\mathtt{out}(u,B)\) 表示 \(B\) 中所有与 \(u\) 直接相连的点的边权和,即 \[ \mathtt{out}(u,B)=\sum_{v \in B} w(u,v) \] \(\mathtt{in}(B)\) 表示原图 \(G\) 的导出子图 \(G[B]\) 中所有点的边权和,即 \[ \mathtt{in}(B) = \dfrac{1}{2} \sum_{u,v\in B} w(u,v) \] \(\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


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录