思路
- 首先,我们看到数据范围1≤n≤12,这么小的n,我们很快就可以想到状压DP。
- 设计
f[i][j]
表示挖第1~i
层,已经挖了的状态为j
时的代价,那么状态转移方程就有了:
f[i][j]=min(f[i][j],f[i-1][k]+(i-1)*cost[k][j]);
- 其中k是j的子集,j是k扩展一层能到达的状态的子集(用
Accessible[k]
表示),Accessible
可以预处理出来:
for(ri i=0;i<1<<n;++i)
{
int ans=0;
for(ri j=0;j<n;++j)
if(i&(1<<j))
{
ans|=1<<j;
for(ri k=0;k<n;++k)
if(w[j][k]!=w[0][0]) ans|=1<<k;
}
Accessible[i]=ans;
}
- 其中
cost[k][j]
表示从状态k
转移到状态j
所需添加的道路的总长度,也可以预处理出来:
for(ri i=0;i<1<<n;++i)
for(ri j=i;j;j=(j-1)&i)
if(j!=i) Calculate_cost(j,i);
int Calculate_cost(int a,int b){
int c=a^b,ans=0;
for(ri i=0;i<n;++i)
if(c&(1<<i))
{
int mi=w[0][0];
for(ri j=0;j<n;++j)
if(a&(1<<j))
mi=min(mi,w[j][i]);
if(mi!=w[0][0]) ans+=mi;
}
return cost[a][b]=ans;
}
- 枚举
i
的子集j
:j=i;j;j=(j-1)&i
;如果a属于b,则有b=a|b
,a=a&b
。 - DP过程:
for(ri i=2;i<=n;++i)
for(ri j=1;j<1<<n;++j)
for(ri k=j;k;k=(k-1)&j)
if(k!=j&&sy(j,Accessible[k]))
f[i][j]=min(f[i][j],f[i-1][k]+(i-1)*cost[k][j]);
- 关于初始状态:因为起点是任意的,所以
for(ri i=0;i<n;++i) f[1][1<<i]=0;
,这样就省去了枚举起点。 - 注意开
long long
。
代码
#include<cstdio>
#include<cstring>
#define int long long
#define ri register int
#define min(a,b) a<b?a:b
#define max(a,b) a>b?a:b
using namespace std;
const int maxn=(1<<12)+21;
int n,m,ans;
int w[15][15];
int f[15][maxn];
int Accessible[maxn];
int cost[maxn][maxn];
int read(){
int x=0;char c=getchar();
while(c>'9'||c<'0') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();
return x;
}
int Calculate_cost(int a,int b){
if(cost[a][b]) return cost[a][b];
int c=a^b,ans=0;
for(ri i=0;i<n;++i)
if(c&(1<<i))
{
int mi=w[0][0];
for(ri j=0;j<n;++j)
if(a&(1<<j))
mi=min(mi,w[j][i]);
if(mi!=w[0][0]) ans+=mi;
}
return cost[a][b]=ans;
}
void pre(){
for(ri i=0;i<1<<n;++i)
{
int ans=0;
for(ri j=0;j<n;++j)
if(i&(1<<j))
{
ans|=1<<j;
for(ri k=0;k<n;++k)
if(w[j][k]!=w[0][0]) ans|=1<<k;
}
Accessible[i]=ans;
}
}
bool sy(int a,int b){
return b==(a|b);
}
void solve(){
memset(f,127,sizeof f);
for(ri i=0;i<1<<n;++i)
for(ri j=i;j;j=(j-1)&i)
if(j!=i) Calculate_cost(j,i);
for(ri i=0;i<n;++i) f[1][1<<i]=0;
for(ri i=2;i<=n;++i)
for(ri j=1;j<1<<n;++j)
for(ri k=j;k;k=(k-1)&j)
if(k!=j&&sy(j,Accessible[k]))
f[i][j]=min(f[i][j],f[i-1][k]+(i-1)*cost[k][j]);
for(ri i=1;i<=n;++i) ans=min(ans,f[i][(1<<n)-1]);
}
signed main(){
n=read(),m=read();
memset(w,127,sizeof w);
while(m--)
{
int u=read()-1,v=read()-1,w1=read();
w[u][v]=w[v][u]=min(w[u][v],w1);
}
pre();
ans=w[0][0];
solve();
printf("%lld",ans);
return 0;
}