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]表示考虑经过S中的点,结尾为u的最大边权和
转移分两种情况
- 枚举一个和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[S∣T][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;
}