传送门
题目大意:给一个带权无向图,求生成树的最小代价.代价 = (起点深度dep[i]-1)*边权edge[i].
n的范围只有12,很容易想到用状压来解决这道题.考虑怎么确定状态.代价只和深度和边权有关.我们可以把最大深度作为dp的阶段,各个点的打通情况作为附加状态.1表示已经打通,0表示尚未打通.再来考虑状态之间如何转移.
假设当前的状态为j.它如果可以从k状态转移过来的话需要满足两个条件.
- k必须是j的子集,这个判断可以用位运算计算.k&j==k.这个应该很好理解.不作解释.
- j必须是k能拓展得到的状态,设k中打通的点能拓展到的状态pre[k],那么同样的j必须是pre[k]的子集.
满足了这两点之后j和k之间就可以进行转移了.为了优化dp的效率,我们还需要预处理出一个road[i][j]数组记录i状态打通到j所需要的最小代价.有了这个数组后我们继续预处理出一个dis[i][j]数组表示i状态到达j状态所需要的最小代价.
这里有一个问题就是说如何判断哪一个打通的节点是深度为i-1的节点,但其实是可以忽略掉这个问题的,因为如果该节点不是深度为i-1的节点,那么它就不会是更优的答案,这是做题常见的一种思考方式,弱化一下限制能更加方便的编写程序.
还有一个值得注意的点是枚举j的子集的时候只需要枚举到j-1即可.
代码
#pragma GCC optimize(2)
#define LL long long
#define pq priority_queue
#define ULL unsigned long long
#define pb push_back
#define mem(a,x) memset(a,x,sizeof a)
#define pii pair<int,int>
#define fir(i,a,b) for(int i=a;i<=(int)b;++i)
#define afir(i,a,b) for(int i=(int)a;i>=b;--i)
#define ft first
#define vi vector<int>
#define sd second
#define ALL(a) a.begin(),a.end()
#define bug puts("-------")
#define mpr(a,b) make_pair(a,b)
#include <bits/stdc++.h>
using namespace std;
const int N = 13;
const int M = 1 << 12;
const int INF = 0x3f3f3f3f;
inline void read(int &a){
int x = 0,f=1;char ch = getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;ch=getchar();}
while(ch<='9'&&ch>='0'){
x=x*10+ch-'0';ch=getchar();}
a = x*f;
}
int n,m,tot,w[N][N],pre[M],dp[N][M],road[M][N];
vi sel[M],dis[M];
void prework(){
fir(i,0,tot){
pre[i] = i;
vi tmp;
fir(j,0,n-1)
tmp.pb(i>>j&1);
fir(j,0,n-1){
if(!tmp[j]) continue;
road[i][j] = 0;
fir(k,0,n-1){
if(w[j][k] == INF) continue;
pre[i] |= (1 << k);
road[i][k] = min(w[j][k],road[i][k]);
}
}
}
fir(j,0,tot){
fir(k,0,j-1){
if((j&k) != k || (j&pre[k]) != j) continue;
sel[j].pb(k);
int sum = 0;
fir(i,0,n-1){
if(( ( (j^k)>>i ) & 1) == 0) continue;
sum += road[k][i];
}
dis[j].pb(sum);
}
}
}
int main(){
cin >> n >> m;
tot = (1<<n)-1;
mem(w,INF);
mem(dp,INF);
mem(road,INF);
fir(i,1,m){
int x,y,z;
cin >> x >> y >> z;
x--;y--;
w[x][y] = min(w[x][y],z);
w[y][x] = min(w[y][x],z);
}
prework();
fir(i,0,n-1)
dp[1][1<<i] = 0;
fir(i,2,n){
fir(j,0,tot){
fir(k,0,(int)sel[j].size()-1){
dp[i][j] = min(dp[i][j],dp[i-1][sel[j][k]]+(i-1)*dis[j][k]);
}
}
}
int ans = INF;
fir(i,1,n) ans = min(ans,dp[i][tot]);
cout << ans;
return 0;
}