/* 思想略。 想说的是:妹纸啊,你这代码效率太低了吧,你说你写了多久?面壁思过去。。。 */ #include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #include <vector> #include <queue> #define mem(a,x) memset(a,x,sizeof(a)) using namespace std; const int NN=24; const int MM=1000; const int INF=0x1fffffff; int n,m,v0,lim,ans,degree; vector<int> adj; struct Edge{ //存储原始边,Kruskal用 int u,v,w; Edge(){} Edge(int uu,int vv,int ww): u(uu),v(vv),w(ww){} }edge[MM]; int ecnt; struct Tree{ //生成树中的边,后期修改用 int u,v,next,w; }te[MM]; int tcnt,head[NN]; void add(int u,int v,int w) { ++tcnt; te[tcnt].u=u; te[tcnt].v=v; te[tcnt].w=w; te[tcnt].next=head[u]; head[u]=tcnt; } char h[NN][12]; int hash(char *s) { for (int i=1; i<=n; i++) if (strcmp(s,h[i])==0) return i; strcpy(h[++n],s); return n; } void init() { ecnt=0; n=0; scanf("%d",&m); char s1[12],s2[12]; strcpy(s1,"Park"); v0=hash(s1); //度限制的点为v0 for (int i=1; i<=m; i++) { int w; scanf("%s%s%d",s1,s2,&w); int u=hash(s1); int v=hash(s2); edge[++ecnt]=Edge(u,v,w); } scanf("%d",&lim); } int p[NN]; int pp(int x) //Kruskal离不开并查集。。。 { if (p[x]!=x) p[x]=pp(p[x]); return p[x]; } int cmp(const void *a,const void *b){ Edge *c=(Edge *)a; Edge *d=(Edge *)b; return c->w-d->w; } void Kruskal() //除去v0点的Kruskal(处理过后的生成树子块不一定是连通的),题目保证有解,点又不多,就没统计边了,怎么方便怎么写,死活不会超时。。。 { tcnt=-1; mem(head,-1); qsort(edge+1,ecnt,sizeof(Edge),cmp); for (int i=1; i<=ecnt; i++) if (edge[i].u==v0 || edge[i].v==v0) adj.push_back(i); for (int i=1; i<=n; i++) p[i]=i; ans=0; for (int i=1; i<=ecnt; i++) { if (edge[i].u==v0 || edge[i].v==v0) continue; if (pp(edge[i].u)==pp(edge[i].v)) continue; p[pp(edge[i].v)]=pp(edge[i].u); ans+=edge[i].w; add(edge[i].u,edge[i].v,edge[i].w); add(edge[i].v,edge[i].u,edge[i].w); } } void link_forest() //刚刚Kruskal时除去了v0点,这里把v0点和已生成的子块连起来,保证有解,就不用判断度degree与度限制lim的关系了,处理过后v0度不超lim的初步生成树。。。 { degree=0; for (int j=0; j<adj.size(); j++) { int i=adj[j]; if (pp(edge[i].u)==pp(edge[i].v)) continue; p[pp(edge[i].v)]=pp(edge[i].u); degree++; ans+=edge[i].w; add(edge[i].u,edge[i].v,edge[i].w); add(edge[i].v,edge[i].u,edge[i].w); edge[i].w=-1; //用过了,-1置之,小优化 } } int me[NN]; int vis[NN]; void get_Maxedge() //从v0点开始BFS一次计算当前生成树中各点到v0点的路径中权值最大的边me[i](此边不能与v0相接的边) { mem(vis,false); vis[v0]=true; te[0].w=0; mem(me,0); queue<int> q; for (int i=head[v0]; i!=-1; i=te[i].next) { q.push(te[i].v); vis[te[i].v]=true; } while (!q.empty()) { int u=q.front(); q.pop(); for (int i=head[u]; i!=-1; i=te[i].next) { int v=te[i].v; if (vis[v] || te[i].w==-1) continue; vis[v]=true; if (te[i].w>te[me[u]].w) me[v]=i; else me[v]=me[u]; q.push(v); } } } void replace() //在v0的度不超lim的情况下试着以与v0相接的边替换已有边,得到更小的生成树 { for (; degree<lim; degree++) { get_Maxedge(); int k,Max_rec=0; for (int j=0; j<adj.size(); j++) //找能替换的最大差值,未用的v0的邻边(v0,u)与me[u]比较 { int i=adj[j]; if (edge[i].w==-1) continue; int u=edge[i].u==v0? edge[i].v:edge[i].u; if (te[me[u]].w-edge[i].w>Max_rec) Max_rec=te[me[u]].w-edge[k=i].w; } if (Max_rec==0) break; //找不到,更新完毕,退出 int u=edge[k].u==v0? edge[k].v:edge[k].u; add(v0,u,edge[k].w); add(u,v0,edge[k].w); ans+=edge[k].w-te[me[u]].w; te[me[u]].w=te[me[u]^1].w=-1; //边被更新,-1标记之 } } int main() { init(); Kruskal(); link_forest(); replace(); printf("Total miles driven: %d\n",ans); return 0; }