[模拟退火][堆优化Prim]2017TG Day2 T2 宝藏

题目描述

参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 n 个深埋在地下的宝藏屋, 也给出了这 n 个宝藏屋之间可供开发的 m 条道路和它们的长度。

小明决心亲自前往挖掘所有宝藏屋中的宝藏。但是,每个宝藏屋距离地面都很远,也就是说,从地面打通一条到某个宝藏屋的道路是很困难的,而开发宝藏屋之间的道路 则相对容易很多。

小明的决心感动了考古挖掘的赞助商,赞助商决定免费赞助他打通一条从地面到某 个宝藏屋的通道,通往哪个宝藏屋则由小明来决定。

在此基础上,小明还需要考虑如何开凿宝藏屋之间的道路。已经开凿出的道路可以 任意通行不消耗代价。每开凿出一条新道路,小明就会与考古队一起挖掘出由该条道路 所能到达的宝藏屋的宝藏。另外,小明不想开发无用道路,即两个已经被挖掘过的宝藏 屋之间的道路无需再开发。

新开发一条道路的代价是:

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

说明

【数据规模与约定】

对于100%的数据:1n12,0m1000,v500000

分析

这题用简单的prim算法可以获得45分

为什么无法拿全分呢?因为prim是类似于贪心的东西,所以最小生成树取到的可能是局部最优解。

看到局部最优解,会模拟退火的同学嘴角肯定会微微上扬!

对于prim算法,我们用堆优化,并且以模拟退火得到的概率忽略堆顶元素

然后就是正常的prim算法了(同时,我们忽略的元素要重新加入堆)

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <queue>
#include <memory.h>
using namespace std;
const int N=1000;
const int Inf=5e5+1;
int g[13][13],intr[13],dep[13];
struct Edge {
    int u,v;
    bool operator <(const Edge a)const {
        return g[u][v]*dep[u]>g[a.u][a.v]*dep[a.u];
    }
}ntcs[N+10];
int n,m;

int Prim(int root) {
    priority_queue<Edge> heap;
    int ans=0;
    memset(intr,0,sizeof intr);
    memset(dep,0,sizeof dep);
    dep[root]=1;intr[root]=1;
    while (!heap.empty()) heap.pop();
    for (int i=1;i<=n;i++)
        if (g[root][i]<Inf) heap.push((Edge){root,i});
    for (int j=1;j<n;j++) {
        Edge edge=heap.top();heap.pop();
        int p=0;
        while (!heap.empty()&&(intr[edge.v]||!((int)rand()%n))) {
            if (!intr[edge.v]) ntcs[++p]=edge;
            edge=heap.top();heap.pop();
        }
        intr[edge.v]=1;dep[edge.v]=dep[edge.u]+1;
        for (int i=1;i<=p;i++)
            heap.push(ntcs[i]);
        for (int i=1;i<=n;i++)
            if (g[edge.v][i]<Inf&&!intr[i])
                heap.push((Edge){edge.v,i});
        ans+=g[edge.u][edge.v]*dep[edge.u];
    }
    return ans;
}

int main() {
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
            g[i][j]=Inf;
    for (int i=1;i<=m;i++) {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        g[u][v]=g[v][u]=min(g[u][v],w);
    }
    int ans=2147483647;
    for (int i=1;i<=500;i++)
        for (int j=1;j<=n;j++)
            ans=min(ans,Prim(j));
    printf("%d",ans);
}
View Code
在日渐沉没的世界里,我发现了你。
原文地址:https://www.cnblogs.com/mastervan/p/9606945.html