当前位置: 代码迷 >> 综合 >> 『图论·状态压缩DP』AT2657:Mole and Abandoned Mine
  详细解决方案

『图论·状态压缩DP』AT2657:Mole and Abandoned Mine

热度:70   发布时间:2023-12-17 11:02:44.0

P r o b l e m \mathrm{Problem} Problem

给一个n个点m条边的无向连通图(不存在自环或重边),每条边有一个边权,要求割掉若干条边,使1到n只有1条路径(不经过重复点),问割掉的边权和最小是多少。

D a t a \mathrm{Data} Data

在这里插入图片描述

S o l u t i o n \mathrm{Solution} Solution

很显然根据数据规模我们可以确定是状态压缩,我们考虑如何转移。

状态很简单,用 f [ s ] [ i ] f[s][i] f[s][i] 表示点即为 s s s ,且 1 1 1 号点最终停留到 j j j 号点,且 i i i j j j 路径只有一条的最大保留数。

状态的转移分成两种:

  • 当前转台转移到新的点: f [ i ] [ j ] + v ( j , k ) → f [ i ∪ k ] [ k ] f[i][j]+v(j,k)→f[i∪k][k] f[i][j]+v(j,k)f[ik][k]
  • 当两个联通块合并且以当前状态为终点时: f [ i ] [ j ] + S [ k ] → f [ i ∪ k ] [ j ] f[i][j]+S[k]→f[i∪k][j] f[i][j]+S[k]f[ik][j]
    S S S表示保留点集为 S S S的所有边的边权和。

接下来考虑如何求解 S S S,显然我们可以采用递推的方法.比如当前状态为 S S S,可以枚举 j j j,用S更新 S ∪ j S∪j Sj

C o d e \mathrm{Code} Code

#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>using namespace std;
const int N = 20;int read(void)
{
    int s = 0, w = 0; char c = getchar();while (c < '0' or c > '9') w |= c == '-', c = getchar();while (c >= '0' and c <= '9') s = s*10+c-48, c = getchar();return w ? -s : s;
}int n, m, sum = 0;
int f[1<<N][N], S[1<<N], val[N][N], in[1<<N];vector <int> a[N];int main(void)
{
    n = read(), m = read();memset(f,-1,sizeof f);for (int i=1,x,y,v;i<=m;++i){
    x = read()-1, y = read()-1, v = read();val[x][y] = val[y][x] = v;sum += v;a[x].push_back(y);a[y].push_back(x);}for (int i=0;i<1<<n;++i)for (int j=0;j<n;++j)if (((i>>j) & 1) == 0 and in[i|(1<<j)] == 0) {
    int cnt = S[i];for (int k=0;k<a[j].size();++k)if ((i >> a[j][k]) & 1) cnt += val[j][a[j][k]];S[i|(1<<j)] = cnt;}f[1][0] = 0;for (int i=0;i<1<<n;++i)for (int j=0;j<n;++j) if (((i >> j) & 1) and f[i][j] != -1){
    if (f[i][j] == -1) continue;for (int k=0;k<n;++k) if (((i >> k) & 1) == 0 && val[j][k]) f[i|(1<<k)][k] = max(f[i|(1<<k)][k],f[i][j]+val[j][k]);int t = (1<<j) | (((1<<n)-1)^i);for (int k=t;k;k=(k-1)&t) if ((k >> j) & 1) f[i|k][j] = max(f[i|k][j],f[i][j]+S[k]);}  cout<<(sum - f[(1<<n)-1][n-1]);return 0;
}