題解-AtCoder ARC-078F Mole and Abandoned Mine

problem

ATC-arc078Fspa

題意概要:給定一個 \(n\)\(m\) 邊簡單無向圖(無自環無重邊),邊有費用,現切去若干條邊,使得從 \(1\)\(n\) 有且僅有一條簡單路徑,求最小化花費。code

\(n\le 15, n-1\le m\le \binom n2\)get

Solution

看到 \(n\leq 15\) 大概就能猜到複雜度是 \(O(3^n)\) 左右的,而後直接思考用斯坦納樹咋解,無果。io

開始思考最終局面的狀況,必定是有一條 \(1\)\(n\) 的路徑,且不能存在其餘路徑鏈接這條路徑上的兩個點。換句話說,就是從每一個點出發且只走非路徑邊的話只能到達一個路徑上的點。class

考慮原問題要求最小化刪邊費用,對應上面的作法容易想到要轉換爲最大化留下來的邊權和test

把圖畫出來,大概是說路徑上每一個點下頭都掛上了若干個集合,而因爲要最大化比邊權和,因此每一個集合內部的全部邊均予以保留。集合

那麼就很容易獲得一個dp:設 \(f[i][S]\) 表示在路徑(最終鏈接 \(1\)\(n\) 的路徑)上走到了 \(i\),而且集合 \(S\) 內的點已經掛在了前頭,那麼有兩種轉移:di

  • 在路徑上更進一步:\(f[i][S] \rightarrow f[j][S\cup \{j\}]\),增長權值爲鏈接 \(i,j\) 的邊權
  • 在當前點掛一個集合:\(f[i][S]\rightarrow f[i][S\cup T]\),增長權值爲集合 \(T\) 內全部邊的邊權和

那麼只要預處理一個 \(g[S]\) 表示集合 \(S\) 內的邊權和,這一步能夠暴力求,複雜度 \(O(2^nn^2)\)思考

再進行轉移,轉移時須要枚舉子集(對於全局而言,枚舉補集的子集和枚舉子集複雜度至關),複雜度 \(O(n3^n)\)co

總複雜度 \(O(2^nn^2+n3^n)\)

Code

#include <cstdio>

#define bin(x) (1<<(x))
#define b(x) (1<<(x)>>1)

inline void cmax(int&x, const int y) {x < y && (x = y);}

const int N = 17, M = 40100;
int f[N][M], g[M];
int mp[N][N];
int n, m;

int main() {
    scanf("%d%d",&n,&m); int Ans = 0;
    for(int i=1,x,y,z;i<=m;++i) {
        scanf("%d%d%d",&x,&y,&z), Ans += z;
        mp[x][y] = mp[y][x] = z;
    }
    for(int s=0;s<bin(n);++s) {
        int&v = g[s];
        for(int i=1;i<=n;++i) if(s & b(i)) 
        for(int j=i+1;j<=n;++j) if(s & b(j)) v += mp[i][j];
    }
    for(int i=1;i<=n;++i)
    for(int j=0;j<bin(n);++j)
        f[i][j] = -1;
    
    f[1][1] = 0;
    for(int S=1;S<bin(n);++S)
    for(int i=1;i<=n;++i) if((S & b(i)) and ~f[i][S]) {
        for(int j=1;j<=n;++j) if((~S & b(j)) and mp[i][j]) cmax(f[j][S|b(j)], f[i][S] + mp[i][j]);
        for(int iS=(bin(n)-1)^S, s = iS; s; s = (s-1) & iS)
            cmax(f[i][S|s], f[i][S] + g[s|b(i)]);
    }
    printf("%d\n", Ans - f[n][bin(n)-1]);
    return 0;
}
相關文章
相關標籤/搜索