问题描述
参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 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
思路分析:
先说一个40分思路:
1.先建图输入数据
2.二进制枚举边
3.枚举时用并查集检验是否联通
4.联通就枚举起点BFS,更新ans
结果4组数据TLE,2组WA
原因:n个节点最多有n(n-1)/2条边,n最大12所以边最多66,二进制就是2^(66),数据量过大,例如#6数据需要15.97s但是输出是正解,做题的时候想成2^(12)< 1W,于是就枚举了。。。
WA原因:如图可能对于当前节点距离近的不如距离远,但是对于下一个节点距离近的好
不过最不开心的是直接就最小生成树是45分。。。
然后是AC思路:退火模拟
AC代码
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
#define inf 2147483647
using namespace std;
int n, m;
int map[13][13];int depth[13];
struct edge {
int u, v;
}; bool operator < (struct edge a, struct edge b) {
return depth[a.u]*map[a.u][a.v]>depth[b.u]*map[b.u][b.v];
}
int search(int source) {
memset(depth, 0, sizeof(depth));
int vis[13]={0};
priority_queue <struct edge> heap;
edge past[1000]; int p = 0;
struct edge e, e2;
int cost = 0;
depth[source]=1; vis[source]=1;
for (int i = 1; i <= n; ++i) {
if (map[source][i]<inf) {
e.u=source; e.v=i;
heap.push(e);
}
} for (int i = 1; i < n; ++i) {
e=heap.top(); heap.pop();
while (!heap.empty()&&((vis[e.v]||rand()%(n)<1))) {//注意这里的判断条件rand()%n<1,即对于一个当前最近点,不选择的几率随着n的增大而减小。
if (!vis[e.v]) past[p++]=e;
//对于跳过了的边,以后还用得上,等待选择结束后再压回优先队列中
e=heap.top(); heap.pop();
} vis[e.v]=1; depth[e.v]=depth[e.u]+1;
if (p-->0) { //压回优先队列
for (;p>=0;--p) {
heap.push(past[p]);
}
}p=0;
for (int i = 1; i <= n; ++i) {
if (map[e.v][i]<inf&&!vis[i]) {
e2.u=e.v; e2.v=i;
heap.push(e2);
}
} cost+=map[e.u][e.v]*depth[e.u];
} return cost;
}
int main()
{
int a, b, c;
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
map[i][j]=inf;
}
}
for (int i = 0; i < m; ++i) {
scanf("%d %d %d", &a, &b,&c);
map[a][b]=map[b][a]=min(c, map[a][b]);
} srand(201208);//瞎写的一个数,应该选什么数都差不多
int MIN = inf;
for (int j = 1; j <1000; ++j) {
//1000次运行是绝对万无一失的,事实上,400次就够了
for (int i=1;i<=n; ++i) {
MIN=min(MIN, search(i));
}
}printf("%d", MIN);
return 0;
}
40分代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <algorithm>
#include <string>
using namespace std;
const int MAX_M=1050; //边的最大值
const int MAX_N=20; //点的最大值
const int INF=0x3f3f3f3f;
int m, n, ans = INF,cnt,input[MAX_N][MAX_N];
//-------------------------------------------------------------------图
struct edge{
int u,v,w,next;
}e[MAX_M];
int p[MAX_N],eid;
void Tinit(){
memset(p,-1,sizeof(p));
eid=0;
return;
}
void insert(int u,int v,int w){
e[eid].u=u;
e[eid].v=v;
e[eid].w=w;
e[eid].next=p[u];
p[u]=eid++;
return;
}
void insert2(int u,int v,int w){
insert(u,v,w);
insert(v,u,w);
}
//------------------------------------------------------------------构建小图
struct e2edge{
int u,v,w,next;
}e2e[MAX_M];
int e2p[MAX_N],e2eid;
void e2Tinit(){
memset(e2p,-1,sizeof(e2p));
e2eid=0;
return;
}
void e2insert(int u,int v,int w){
e2e[e2eid].u=u;
e2e[e2eid].v=v;
e2e[e2eid].w=w;
e2e[e2eid].next=e2p[u];
e2p[u]=e2eid++;
return;
}
void e2insert2(int u,int v,int w){
e2insert(u,v,w);
e2insert(v,u,w);
}
//------------------------------------------------------------------并查集
int dad[MAX_N];
void Binit (){
for(int i=0;i<MAX_N;i++)dad[i]=i;
return;
}
int Bgetdad(int i){
if(dad[i]==i)return i;
return dad[i]=Bgetdad(dad[i]);
}
//---------------------------------------------------------------------BFS
int d[MAX_N];
void BFS(int st){
int tans=0;
memset(d,INF,sizeof(d));
queue<int>q;
q.push(st);
d[st]=0;
while(!q.empty()){
int tmp=q.front();
q.pop();
for (int i=e2p[tmp]; i!= -1; i = e2e[i].next) {
if(d[e2e[i].v]==INF){
d[e2e[i].v]=d[tmp]+1;
q.push(e2e[i].v);
tans+=d[e2e[i].v]*e2e[i].w;
}
}
}
ans=min(ans,tans);
}
//--------------------------------------------------------------------------main
int main(){
memset(input,INF,sizeof(input));
Tinit();
cin>>n>>m;
for(int i=1;i<=m;i++){
int a,b,v;
cin>>a>>b>>v;
if(a<b){int t=a;a=b;b=t;}
input[a][b]=min(input[a][b],v);
}
for(int i=1;i<=n;i++){
for(int j=i-1;j>=1;j--){
if(input[i][j]!=INF){
insert2(i,j,input[i][j]);
cnt++;
}
}
}
cout<<cnt;
int debuguse=0;
for(int i=0;i<=(1<<cnt);i++){
e2Tinit();
Binit();
for(int j=0;j<=cnt;j++){
if(i&(1<<j)){
if(Bgetdad(e[j*2].u)!=Bgetdad(e[j*2].v))dad[Bgetdad(e[j*2].u)]=Bgetdad(e[j*2].v);//双向图
e2insert2(e[j*2].u,e[j*2].v,e[j*2].w);
}
}
int t=Bgetdad(1);
for(int i=1;i<=n;i++)if(Bgetdad(i)!=t){t=-1;}
if(t==-1){continue;}
for(int m=1;m<=n;m++){debuguse++;BFS(m);}
}
cout<<ans;
return 0;
}