[关键字]:最小生成树
[题目大意]:在一个图里找到一颗生成树,要求选定一个根后从这个根到每一个点都走一边每经过一个点i需要停留di分钟每条边需要ei分钟根还要额外加两遍所得值最小。
//====================================================================================================
[分析]:因为每加入一条边(x,y),所增加的代价就是ei+dy+ei+dx,又因为所有边都加入后作为根的那个点少加了一次为了让最终结果最小需要选取最小的点作为根并让ans加上它的权值。所以可以以权值最小的点位起点,求最小生成树更新节点时利用上面所示的公式。
[代码]:
View Code
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int MAXN=12000; const int MAXM=220000; const int INF=0x7fffffff; struct edge { int y,d,next; }e[MAXM]; struct node { int p,d; }; int first[MAXN],a[MAXN],d[MAXN]; int n,m,tot,S,ans=0; bool vis[MAXN]; priority_queue<node> q; bool operator < (node a,node b) {return a.d>b.d;} void Add(int x,int y,int z) { e[++tot].y=y; e[tot].d=z; e[tot].next=first[x]; first[x]=tot; } void Init() { scanf("%d%d",&n,&m); int Min=INF; for (int i=1;i<=n;++i) { scanf("%d",&a[i]); if (Min>a[i]) Min=a[S=i]; } for (int i=1;i<=m;++i) { int x,y,z; scanf("%d%d%d",&x,&y,&z); Add(x,y,z),Add(y,x,z); } } void Solve() { memset(vis,0,sizeof(vis)); memset(d,100,sizeof(d)); node u,next; u.p=S,u.d=a[S],d[S]=a[S]; q.push(u); while (!q.empty()) { u=q.top(),q.pop(); int Min=u.d,Minj=u.p; if (vis[Minj]) continue; vis[Minj]=1,ans+=Min; for (int i=first[Minj];i;i=e[i].next) { int v=e[i].y; if (!vis[v] && d[v]>a[Minj]+a[v]+2*e[i].d) { d[v]=a[Minj]+a[v]+2*e[i].d; next.p=v,next.d=d[v]; q.push(next); } } } printf("%d\n",ans); } int main() { freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); Init(); Solve(); return 0; }