http://poj.org/problem?id=1639 //最小限度生成树 /* 解题思路: 1)将根节点从图中去除掉 2)对去除根节点的图求MST,注意这里去除根后的图可能是不连通的,所以计算MST的时候要对每个连通图都进行计算 这里我选择用Kruscal算法+并查集求MST.求完后用DFS将属于同一个连通分量的点放在一个数组里面,并得到连通分量的个数。 3)针对每一个连通分量,选择从根节点到这个连通分量里的节点的具有最小权值的边加入图中,假设有deep个连通分 量则一共需要加deep条边 4)由1)2)3)步骤即得到一棵deep最小限度生成树 5)假设题目限定的停车位数是k,那么还需要找k - deep条从根节点出发的边,所以循环k- deep次,每次做 如下操作: a)假设对于节点k, 边[root, j, w]不在当前树中(权值为w), 那么如果把这条边加入树中就会构成一条环 b)假设这个环中不是与根直接相连的具有最大权值的边为[from, to, weight] 遍历寻找具有最大weight - w值的边,如果这个差值大于0,则将这条边[root, j]加入树中并去除边[from, to],如果差值小于等于0则退出 */ #include<iostream> #include<string> #include<queue> #include<map> #include<algorithm> using namespace std; #define N 25 #define min(a,b) ((a)<(b)?(a):(b)) #define MAX 0x7fffffff struct node { int id; int u, v; int len; const bool operator<(const node &b)const { return len < b.len; } }edge[N*N]; map<string,int> name; int pre[N]; int data[N][N];//存储两点之间的距离 int num[N]; bool dd[N][N];//标志是否在最小生成树 bool mark[N]; int stack[N][N];//存储连通分量 int n, k, size; int sum, deep; void init() { int w, u, v, m, i; string s1, s2; map<string,int>::iterator mp; name["Park"] = 0; memset(dd,false,sizeof(dd)); memset(stack,0,sizeof(stack)); memset(mark,false,sizeof(mark)); memset(data,0,sizeof(data)); memset(num,0,sizeof(num)); deep = 0; sum = 0; size = 0; n = 0; cin>>m; for(i=1; i<=m; i++) { cin>>s1>>s2>>w; mp = name.find(s1); if(mp != name.end()) { u = mp->second; } else { n++; name[s1] = n; u = n; } mp = name.find(s2); if(mp != name.end()) v = mp->second; else { n++; name[s2] = n; v=n; } if(u && v)//表示去除根结点的(最小)生成树 { edge[size].u = u; edge[size].v = v; edge[size].len = w; size++; } if(!data[u][v]) data[u][v]=data[v][u] = w; else { data[u][v] = min(w, data[u][v]); } } cin>>k; } int find_set(int i) { if(i == pre[i]) return i; return pre[i] = find_set(pre[i]); } void mst()//用Kruskal算法算最小生成树 { int p, q, i; sort(edge,edge+size); for(i=1; i <= n; i++) pre[i]=i; for(i=0; i < size; i++) { p=find_set(edge[i].u); q=find_set(edge[i].v); if(p != q) { dd[edge[i].u][edge[i].v]=true; dd[edge[i].v][edge[i].u]=true; pre[p]=q; sum += edge[i].len; } } } void dfs(int i)//寻找连通分量 { num[i] = deep;//标志这个结点属于deep这个连通分量 stack[deep][0]++; stack[deep][stack[deep][0]]=i; for(int j=1; j<=n; j++) if(dd[i][j] && !num[j]) dfs(j); } bool tdfs(int i)//寻找有i的这个环 { mark[i] = true; if(i==0) return true; for(int j=0; j<=n; j++) { if(dd[i][j] && data[i][j] && !mark[j]) { pre[j]=i; mark[j]=true; if(tdfs(j)) return true; mark[j]=false; } } return false; } void solve() { mst();//求"最小生成树" int i, j; for(i=1; i<=n; i++)//寻找连通分量,将同一个连通分量的点放在stack[deep]中 { if(!num[i]) { deep++; dfs(i); } } for(i=1; i<=deep; i++)//在每一个连通分量中找一个离0点最近的点,添加到生成树中 { int mi=MAX, mj; for(j=1; j<=stack[i][0]; j++) { if(mi>data[0][stack[i][j]] && data[0][stack[i][j]]) { mi=data[0][stack[i][j]]; mj=stack[i][j]; } } dd[0][mj]=true; dd[mj][0]=true; sum+=mi; } priority_queue<node> myque; node stem; int from, to, w; for(i=1; i<=k-deep; i++) { while(!myque.empty()) myque.pop(); for(j=1; j<=n; j++) { if(!dd[0][j] && data[0][j]) { memset(mark,false,sizeof(mark)); memset(pre,0,sizeof(pre)); from=to=0; w=0; tdfs(j); int tp=0; while(pre[tp]) { if(data[pre[tp]][tp]>w) { w=data[pre[tp]][tp]; from=pre[tp]; to=tp; } tp=pre[tp]; } if(from && to && data[from][to] > data[0][j]) { stem.id=j; stem.u=from; stem.v=to; stem.len=data[from][to] - data[0][j]; myque.push(stem); } } } if(myque.empty()) break; stem=myque.top(); sum-=stem.len; dd[stem.v][stem.u]=dd[stem.u][stem.v]=false; dd[stem.id][0]=dd[0][stem.id]=true; } cout<<"Total miles driven: "<<sum<<endl; } int main() { // freopen("in.txt","r",stdin); init(); solve(); return 0; }