参与考古挖掘的小明获得了一份藏宝图,藏宝图上标出了 \(n\) 个深埋在地下的宝藏屋, 也给出了这 \(n\) 个宝藏屋之间可供开发的 \(m\) 条道路和它们的长度。html
小明决心亲自前往挖掘全部宝藏屋中的宝藏。可是,每一个宝藏屋距离地面都很远, 也就是说,从地面打通一条到某个宝藏屋的道路是很困难的,而开发宝藏屋之间的道路 则相对容易不少。c++
小明的决心感动了考古挖掘的赞助商,赞助商决定免费赞助他打通一条从地面到某 个宝藏屋的通道,通往哪一个宝藏屋则由小明来决定。git
在此基础上,小明还须要考虑如何开凿宝藏屋之间的道路。已经开凿出的道路能够 任意通行不消耗代价。每开凿出一条新道路,小明就会与考古队一块儿挖掘出由该条道路 所能到达的宝藏屋的宝藏。另外,小明不想开发无用道路,即两个已经被挖掘过的宝藏 屋之间的道路无需再开发。数组
新开发一条道路的代价是:spa
\(\mathrm{L} \times \mathrm{K}\)code
\(L\)表明这条道路的长度,\(K\)表明从赞助商帮你打通的宝藏屋到这条道路起点的宝藏屋所通过的 宝藏屋的数量(包括赞助商帮你打通的宝藏屋和这条道路起点的宝藏屋) 。htm
请你编写程序为小明选定由赞助商打通的宝藏屋和以后开凿的道路,使得工程总代 价最小,并输出这个最小值。blog
输入格式
第一行两个用空格分离的正整数 \(n,m\),表明宝藏屋的个数和道路数。开发
接下来 \(m\) 行,每行三个用空格分离的正整数,分别是由一条道路链接的两个宝藏 屋的编号(编号为 \(1-n\)),和这条道路的长度 \(v\)。get
输出格式
一个正整数,表示最小的总代价。
【数据规模与约定】
对于 \(\%20\)的数据: 保证输入是一棵树,\(1 \le n \le 8\),\(v \le 5000\) 且全部的 \(v\) 都相等。
对于 \(\%40\)的数据: \(1 \le n \le 8\),\(0 \le m \le 1000\),\(v \le 5000\) 且全部的 \(v\)都相等。
对于 \(\%70\)的数据: \(1 \le n \le 8\),\(0 \le m \le 1000\),\(v \le 5000\)
对于 \(\%100%\)的数据: \(1 \le n \le 12\),\(0 \le m \le 1000\),\(v \le 500000\)
好吧其实这题仍是有点难的,蒟蒻我当时仍是在考场上拿到这题一脸懵逼。
看到这题的规模,\(n \le 12\),通常就会有几种想法:爆搜,状压(还有大佬写的模拟退火。。。是本弱弱不会的了) 。
由题意可得,咱们求完以后会是一棵树,图上找一棵树。
首先,咱们想到某一个点\(i\) 做为这个图中树的根,对于其余点,咱们关心其余点到起点的距离。
对于第一部分分,咱们只须要对每个根作一次树形DP便可。
以后变成了一个图,咱们考虑状态压缩。
咱们记 \(f[S][i]\) , 表示咱们对于状态为 \(S\) 的点集,最深层数为 \(i\) 。
而后咱们能够稍微思考一下,对于点集 \(S\) ,他能够由本身的子点集 \(S1\) 转移而来, ,因而,咱们就有:
\(f[S][d] = min{f[S1][d-1] + (d-1) \times transfer[S1][S]}, S1\subset S\)
解释一下,这个式子至关于就是把\(S1\) 中的点,向外扩展一层,扩展完点集为\(S\)。
\(transfer[S1][S]\) 表示从 \(S1\)到 \(S\) 的最小价值。
咱们考虑S中点\(i\), 不在\(S1\) 中, 那么从\(i\) 转移到\(S1\) 的最小价值为:
\(W[i][S1] = min(e[i][j]), j \in S1\)
可是事实上这个W数组是不用写出来的,一次次加上去就行了。
而后
\(transfer[S1][S] = \sigma W[i][S1] ,i\in S, i\notin S1\)
可是吧,不知道你们有没有这个困惑
在我写完时候也有点纠结(纠结了很久,写完才想到)
其实咱们考虑一个点
我不会画画(偷懒),引用了大佬的博客GoldenPotato的OI世界
考虑这个图里,咱们以图中给的为根,若是咱们统计k=1时候没有加最右边的一个点,到k=2的状态时把这个点算进去,那不是距离根节点只有一个宝藏屋的边要\(\times 2\)
显然是不对的。
可是咱们能够在其余点为根的状态中,把这个点加进去,就必定会有最优的解。
因此咱们的答案统计为: \(ans = min{f[i][(1<<n)-1], i \in [1,n]}\)
还有一个小技巧,就是枚举子集,
for(int s1=s; s1; s1=(s1-1)&s)
复杂度这样就从\(n^2\times 4^n\) 降到 \(n^2 \times 3^n\)
因而乎,贴代码。
若是我哪里有疏漏,欢迎你们指正~
#include<bits/stdc++.h> using namespace std; #define rep(i,a,b) for(int i=a;i<=b;i++) #define _(d) while(d(isdigit(ch=getchar()))) template <class T> void g(T&t){T x,f=1;char ch;_(!)ch=='-'?f=-1:f;x=ch-48;_()x=x*10+ch-48;t=f*x;} typedef long long ll; const int N=14; int n, m, inf, tr[1<<N][1<<N], e[N][N];ll f[N][1<<N]; int main(){ g(n), g(m); memset(e, 63, sizeof(e)); inf = e[0][0]; rep(i,1,m){ int x, y, z; g(x), g(y), g(z); e[x][y] = e[y][x] = min( e[x][y], z ); } rep(s, 0, (1<<n)-1){ for(int s1=s; s1; s1=(s1-1)&s){ int tmp= s ^ s1; bool flag=0; rep(i, 1, n){ if( 1<<(i-1) & tmp ){ int tt= inf; rep(j, 1, n){ if( 1<<(j-1) & s1){ tt= min( tt, e[i][j] ); } } if( tt == inf ){ flag = 1; break; } tr[s1][s] += tt; } } if( flag ){ tr[s1][s] = inf; } } } memset(f, 63, sizeof(f)); ll ans=f[0][0]; rep(i, 1, n) f[1][1<<(i-1)] = 0; rep(i, 2, n){ rep( s, 0, (1<<n)-1 ) for(int s1=s; s1; s1=(s1-1) & s ){ if(tr[s1][s]!=inf) f[i][s]=min(f[i][s], f[i-1][s1] + tr[s1][s]*(i-1) ); } } rep(i, 1, n) ans=min(ans, f[i][(1<<n)-1]); printf("%lld\n",ans); return 0; }