AT2657 [ARC078D] Mole and Abandoned Mine

https://www.luogu.com.cn/problem/AT2657

好像随机化打乱边表建最小生成树也可以过
麻了

首先肯定是状压DP
考虑转换为保留的边最多
f [ S ] [ u ] 表 示 考 虑 经 过 S 中 的 点 , 结 尾 为 u 的 最 大 边 权 和 f[S][u]表示考虑经过S中的点,结尾为u的最大边权和 f[S][u]Su
转移分两种情况

  • 枚举一个和u相连的点v,然后走过去 f [ S ] [ u ] − > f [ S ∣ { v } ] [ v ] f[S][u]->f[S|\{v\}][v] f[S][u]>f[S{v}][v]
  • u u u和一个集合拼上 f [ S ] [ u ] − > f [ S ∣ T ] [ u ] f[S][u]->f[S|T][u] f[S][u]>f[ST][u]
    第二条的意思就是加上了T中的点,不过因为T和S只靠u来联通,所以路径数量还是1,并不会影响

代码实现很简单
code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
void maxx(ll &x, ll y) {
    x = max(x, y);
}
const int N = 17;
const int SN = (1 << N) + 5;
const ll INF = 1e16;
int n, m, g[N][N];
ll sum[SN], f[SN][N];
int main() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= m; i ++) {
        int u, v, c;
        scanf("%d%d%d", &u, &v, &c);
        g[u][v] = g[v][u] = c;
    }
    int lim = (1 << n);
    for(int S = 1; S < lim; S ++) {
        for(int i = 1; i <= n; i ++) if((S >> (i - 1)) & 1) {
            for(int j = i + 1; j <= n; j ++) if((S >> (j - 1)) & 1) {
                sum[S] += g[i][j];
            }
        }
    }
    for(int S = 0; S < lim; S ++)
        for(int i = 0; i <= n; i ++) f[S][i] = - INF;
    f[1][1] = 0;
    for(int S = 1; S < lim; S ++) {
        for(int u = 1; u <= n; u ++) if((S >> (u - 1)) & 1) if(f[S][u] != - INF) {
            for(int v = 1; v <= n; v ++) if(!((S >> (v - 1)) & 1) && g[u][v]) {
                maxx(f[S | (1 << (v - 1))][v], f[S][u] + g[u][v]);
            }
            
            int Sp = (lim - 1) ^ S;
            for(int T = Sp; T != 0; T = (T - 1) & (Sp)) {
                maxx(f[S | T][u], f[S][u] + sum[T | (1 << (u - 1))]);
            }
        }
    }
    printf("%lld", sum[lim - 1] - f[lim - 1][n]);
    return 0;
}
posted @ 2021-09-13 21:59  lahlah  阅读(42)  评论(0编辑  收藏  举报