专栏文章
[ARC078F] Mole and Abandoned Mine
AT_arc078_d题解参与者 3已保存评论 2
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 2 条
- 当前快照
- 1 份
- 快照标识符
- @miqtrqrl
- 此快照首次捕获于
- 2025/12/04 10:36 3 个月前
- 此快照最后确认于
- 2025/12/04 10:36 3 个月前
[ARC078F] Mole and Abandoned Mine
前言
看了前两篇题解,一篇时间复杂度不对,一篇排版太乱根本不可读,于是我打算写一篇更好的题解。
题意
个点 条边的简单带权无向连通图,要求割掉若干条边,使 到 只有 条路径,问割掉的边权和最小是多少。
题解
发现割边很难判断是否只有一条路径,所以尝试将题意转为保留边权和最大的边集。
通过观察发现,最优解一定把这张图分成若干个连通块,然后用一条路径串起来,类似下图:

我们可以 预处理出每个连通块的边权和:设
看到 ,考虑状压 DP。
设 表示已经连了 中的点,路径走到 的最大边权和,考虑如何转移。
我们用刷表法实现转移:
时间复杂度 ,无法通过。(常数小可能过?)
我们可以用一个类似前缀和优化的东西。设 ,那么原式变为 。
可以 处理出来,转移优化至 。
于是总的时间复杂度优化到 ,足以通过本题。
代码
CPP#include<bits/stdc++.h>
using namespace std;
const int N=20,NN=40010;
int n,m,f[NN][N],g[NN][N],W[NN],e[N][N],sum;
int main(){
cin>>n>>m;
memset(e,-1,sizeof(e));
for(int i=1,u,v,w;i<=m;i++){
scanf("%d%d%d",&u,&v,&w);
e[u][v]=e[v][u]=w;
sum+=w;
}
for(int S=0;S<(1<<n);S++){//预处理连通块
for(int i=1;i<=n;i++){
if(((1<<i)&(S<<1))==0)continue;
for(int j=i+1;j<=n;j++){
if(((1<<j)&(S<<1))==0)continue;
if(~e[i][j])W[S]+=e[i][j];
}
}
}
memset(f,-0x3f,sizeof(f));
memset(g,-0x3f,sizeof(g));
for(int S=1;S<(1<<n);S++)if(S&1)f[S][1]=W[S];
for(int S=1;S<(1<<n);S++){
for(int i=1;i<=n;i++){//求 g
if(((1<<i)&(S<<1))==0)continue;
for(int j=1;j<=n;j++){
if((1<<j)&(S<<1))continue;
if(~e[i][j])g[S][j]=max(g[S][j],f[S][i]+e[i][j]);
}
}
for(int SS=(1<<n)-1-S;SS;SS=(SS-1)&((1<<n)-1-S)){//刷表法求 f
for(int j=1;j<=n;j++){
if(((1<<j)&(SS<<1))==0)continue;
if(!S&&j!=1)continue;
f[S|SS][j]=max(f[S|SS][j],g[S][j]+W[SS]);
}
}
}
cout<<sum-f[(1<<n)-1][n];
}
相关推荐
评论
共 2 条评论,欢迎与作者交流。
正在加载评论...