原题链接:https://www.luogu.org/problemnew/show/P3959
参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 n 个深埋在地下的宝藏屋, 也给出了这 n 个宝藏屋之间可供开发的 m 条道路和它们的长度。
小明决心亲自前往挖掘所有宝藏屋中的宝藏。但是,每个宝藏屋距离地面都很远, 也就是说,从地面打通一条到某个宝藏屋的道路是很困难的,而开发宝藏屋之间的道路 则相对容易很多。
小明的决心感动了考古挖掘的赞助商,赞助商决定免费赞助他打通一条从地面到某 个宝藏屋的通道,通往哪个宝藏屋则由小明来决定。
在此基础上,小明还需要考虑如何开凿宝藏屋之间的道路。已经开凿出的道路可以 任意通行不消耗代价。每开凿出一条新道路,小明就会与考古队一起挖掘出由该条道路 所能到达的宝藏屋的宝藏。另外,小明不想开发无用道路,即两个已经被挖掘过的宝藏 屋之间的道路无需再开发。
新开发一条道路的代价是:
L×K
L代表这条道路的长度,K代表从赞助商帮你打通的宝藏屋到这条道路起点的宝藏屋所经过的 宝藏屋的数量(包括赞助商帮你打通的宝藏屋和这条道路起点的宝藏屋) 。
请你编写程序为小明选定由赞助商打通的宝藏屋和之后开凿的道路,使得工程总代 价最小,并输出这个最小值。
第一行两个用空格分离的正整数 n 和 m,代表宝藏屋的个数和道路数。
接下来 m 行,每行三个用空格分离的正整数,分别是由一条道路连接的两个宝藏 屋的编号(编号为 1~n),和这条道路的长度 v。
输出共一行,一个正整数,表示最小的总代价。
4 5
1 2 1
1 3 3
1 4 1
2 3 4
3 4 1
4
4 5
1 2 1
1 3 3
1 4 1
2 3 4
3 4 2
5
小明选定让赞助商打通了1号宝藏屋。小明开发了道路 1→2 ,挖掘了 2 号宝藏。开发了道路 1→4 ,挖掘了4号宝藏。还开发了道路4→3,挖掘了3号宝藏。工程总代价为:1×1+1×1+1×2=4
小明选定让赞助商打通了 1 号宝藏屋。小明开发了道路 1→2 ,挖掘了 2 号宝 藏。开发了道路 1→3 ,挖掘了 3 号宝藏。还开发了道路 1→4 ,挖掘了 4 号宝 藏。工程总代价为:1×1+3×1+1×1=5
对于 20%的数据: 保证输入是一棵树,1≤n≤8 ,v≤5000 且所有的 v 都相等。
对于 40%的数据:1≤n≤8 ,0≤m≤1000 ,v≤5000 且所有的 v 都相等。
对于 70%的数据:1≤n≤8 ,0≤m≤1000 ,v≤5000
对于 100%的数据:1≤n≤12 ,0≤m≤1000 ,v≤500000
博主NOip打了一发Prim成功get15分。。。orz我是真的弱。
为了解心头之恨博主励志用Prim把这玩意儿A掉,但是Prim实际上是一种贪心,不是特别适用这道题,所以我们给贪心升个级,经观察,此题数据量极少。。。
然后就有了——模拟退火!!!
是的,当我们Prim的时候,我们可以跳过堆顶的一些边,以一定的几率接受一个不优的边,从而跳出局部最优解,跟退火有异曲同工之妙啊!我们只需要退个250次就A了!!!
#include
using namespace std;
const int M=15;
struct sd{int f,t;};
int n,m,mmp[M][M],dep[M],vis[M];
bool operator <(sd a,sd b)
{return dep[a.f]*mmp[a.f][a.t]>dep[b.f]*mmp[b.f][b.t];}
priority_queue<sd>dui;
sd used[1005];
void in()
{
memset(mmp,63,sizeof(mmp));
int a,b,c;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i)
scanf("%d%d%d",&a,&b,&c),mmp[a][b]=mmp[b][a]=min(mmp[a][b],c);
}
int sa(int root)
{
memset(dep,0,sizeof(dep));
memset(vis,0,sizeof(vis));
memset(used,0,sizeof(used));
while(!dui.empty())dui.pop();
int p=0,ans=0;sd e;
vis[root]=dep[root]=1;
for(int i=1;i<=n;++i)
if(mmp[root][i]<mmp[0][0])
dui.push((sd){root,i});
for(int i=1;i<n;++i)
{
e=dui.top();dui.pop();
while(!dui.empty()&&(vis[e.t]||rand()%n<1))
{
if(!vis[e.t])used[++p]=e;
e=dui.top();dui.pop();
}
vis[e.t]=1;dep[e.t]=dep[e.f]+1;
while(p>0)
dui.push(used[p--]);
for(int i=1;i<=n;++i)
if(!vis[i]&&mmp[e.t][i]<mmp[0][0])
dui.push((sd){e.t,i});
ans+=mmp[e.f][e.t]*dep[e.f];
}
return ans;
}
void ac()
{
int ans=1e9;
for(int i=1;i<=250;++i)
for(int j=1;j<=n;++j)
ans=min(ans,sa(j));
printf("%d",ans);
}
int main()
{
srand(19260817);
in();ac();
return 0;
}