最小生成树算法模板

//kruskal算法模板 #include<iostream> #include<algorithm> using namespace std; #define M 100 //最多边数 #define N 100 //最多顶点数 typedef struct edge { int a; int b; int value; }edge; edge edges[M]; int final[N]; //存储父节点 int nodecount[N]; //存储该节点孩子结点的个数 bool cmp(edge a,edge b) { return a.value<b.value; } int findp(int n) //寻找父节点 { if(final[n]==n) return n; else final[n]=findp(final[n]); return final[n]; } bool Union(int x,int y) //合并 { int rootx=findp(x); int rooty=findp(y); if(rootx==rooty) return false; else if(nodecount[rootx]<=nodecount[rooty]) { final[rootx]=rooty; nodecount[rooty]+=nodecount[rootx]; } else { final[rooty]=rootx; nodecount[rootx]+=nodecount[rooty]; } return true; } int main () { //freopen("1.txt","r",stdin); int num=0; int n,m,sum; int i; while ( scanf ( "%d%d", &n, &m ) != EOF ) { num=0; //记录生成树中的边的数目 sum=0; for(i=1;i<=m;i++) { scanf("%d%d%d",&edges[i].a,&edges[i].b,&edges[i].value); } for(i=1;i<=n;i++) //初始化 { final[i]=i; nodecount[i]=1; } sort(edges+1,edges+m+1,cmp); //排序 for(i=1;i<=m;i++) //遍历所有的边 { if(Union(edges[i].a,edges[i].b)) //合并 { num++;//记录最小生成树的边数 sum+=edges[i].value; printf("%d->%d/n",edges[i].a,edges[i].b); } if(num==n-1) //找到了最小生成树 break; } printf("%d/n",sum); } return 0; }

//prim算法模板 #include<iostream> using namespace std; #define M 100 //顶点数 #define INF 0x7fffffff int graph[M][M]; //用于存放图结构 bool visit[M]; //用于标志是否与已标记 int lowcost[M]; //标记最小边 int closest[M]; int n; //结点个数 int prim(int src) { int sum = 0; int minData; int i,j; int v; memset(visit,0,sizeof(visit)); for(i=1;i<=n;++i) { lowcost[i]=graph[src][i]; closest[i]=src; } lowcost[src] = 0; //初始化源点的最短距离为0 visit[src] = true; for(i=1;i<n;++i) { minData = INF; for(j=1;j<=n;++j) { if(!visit[j] && lowcost[j]<minData) { v = j; minData = lowcost[j]; } } visit[v] = true; sum +=lowcost[v]; for(j=1;j<=n;++j) { if(!visit[j] && graph[v][j]<INF && lowcost[j]>graph[v][j]) //prim算法仅在此处与Dijkstra算法不同 { lowcost[j] =graph[v][j]; closest[j]=v; } } } return sum; } int main() { // freopen("in.txt","r",stdin); int i,j,m,k,w; while(scanf("%d",&n)!=EOF) { scanf("%d",&m);//输入边数 for(i=1;i<=n;i++)//初始化为最大值 for(j=1;j<=n;j++) graph[i][j]=INF; for(i=1;i<=m;i++) { scanf("%d %d %d",&j,&k,&w); graph[j][k]=graph[k][j]=w; } int minsum=prim(1); for(i=2;i<=n;i++) printf("%d->%d/n",i,closest[i]); printf("%d/n",minsum); } return 0; }

最小K度生成树:

//最小限度生成树 /* 解题思路:   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; }

最小树形图:

/* 对于除了根节点外的每个节点 i,求出权值最小的入(1)边 (prev[i], i)。如果这些入边不构成环, 那么入边的集合 E 即为所求。假设其中的一个环为 v0 <- v1 <- …… <- vk <- v0, 那么如下更新原图: w(v[0], u) = min{w(v[j], u)} (0 <= j <= k) w(u, v[0]) = min{w(u, v[j]) - w(prev[j], j)} (0 <= j <= k) 并且 res 的值增加环中所有边的权值 重复以上操作,直到 E 中不存在环。此时 res 就是最小树形图的边权值之和。 w(u, v0) 之所以要修改为 min{w(u, v[j]) - w(prev[j], j)},因为如果环外有个点 u 连向环中 某一点 v[j],那么加上之前算入 res 总和的 w(prev[j], j),正好变成 w(u, v[j])。 */ //对于有向图求最小生成树 #include <algorithm> #include <cfloat> #include <cstdio> using namespace std; #define INT_MAX 0x7fffffff const int N = 100; bool visited[N]; int n; double a[N][N]; //邻接矩阵 void DFS(int x)//判断是否能构成最小树形图 { visited[x] = true; for (int i = 0; i < n; ++i) if (!visited[i] && a[x][i] < DBL_MAX-1) DFS(i); } double ZhuYongJin_LiuZhenHong() { //朱永津、刘振宏 static bool flag[N]; static int prev[N]; double res = 0; fill_n(flag, n, false); for(;;) { int i; for (i = 1; i < n; ++i)//对于每一个结点(除了根结点)求出以它为入度的最小权值的边 { if (flag[i]) continue; //该点已被删除 prev[i] = i; a[i][i] = INT_MAX; //删除自环 for (int j = 0; j < n; ++j) if (!flag[j] && a[j][i] < a[prev[i]][i]) prev[i] = j; } for (i = 1; i < n; ++i) { if (flag[i]) continue; //寻找环 int j = i; visited[0] = true; fill_n(visited+1, n-1, false); do { visited[j] = true, j = prev[j]; }while (!visited[j]); if (!j) continue; //包含根节点 i = j; res += a[prev[i]][i]; for (j = prev[i]; j != i; j = prev[j]) { flag[j] = true; //删除环中除了 i 之外的所有节点 res += a[prev[j]][j]; } //w(j, i) 减去 w(prev(i), i) for (j = 0; j < n; ++j) if (!flag[j] && a[j][i] < DBL_MAX-1) a[j][i] -= a[prev[i]][i]; //修改和环关联的边的权值 for (int k = 0; k < n; ++k) if (!flag[k]) for (j = prev[i]; j != i; j = prev[j]) { if (a[j][k] < a[i][k]) a[i][k] = a[j][k]; if (a[k][j] < DBL_MAX-1 && a[k][j]-a[prev[j]][j] < a[k][i]) a[k][i] = a[k][j]-a[prev[j]][j]; } break; } if (i == n) //不再存在环 { for (i = 1; i < n; ++i) if (!flag[i]) res += a[prev[i]][i]; break; } } return res; } int main() { int m, u, v; double w; freopen("in.txt","r",stdin); while (scanf("%d%d", &n, &m) != EOF) { for (int i = 0; i < n; ++i) fill_n(a[i], n, DBL_MAX); for (i=0; i<m; i++) { scanf("%d%d%lf", &u, &v, &w); a[u][v] = w; } fill_n(visited, n, false); DFS(0); for (i = 1; i < n; ++i) if (!visited[i]) { puts("最小树形图不存在"); goto L1; } printf("%lf/n", ZhuYongJin_LiuZhenHong()); L1:; } return 0; }

最优生成树:

//最优比率生成树(总费用/总长度要最大)假设总费用cost/总长度len=k则cost=len*k,其中k要尽量的大,所以我们可以从k从小开始,利用二分的方法 #include<stdio.h> #include<string.h> #include<math.h> #define M 1200 #define INF 0x7fffffff #define min 1e-5 #define zero(x) (((x)>0?x:-(x))<min) struct point { int x,y,h; }; point p[M]; double mat[M][M],len[M][M];// int cost[M][M];//两点之间的高度差 double d[M]; bool visit[M]; double prim(int u,int n) { int i; double ans=0,m; for(i=0;i<n;i++) d[i]=INF,visit[i]=false; d[u]=0; while(u!=-1) { visit[u]=true; ans+=d[u]; for(i=0;i<n;i++) if(!visit[i] && mat[u][i]<d[i]) d[i]=mat[u][i]; m=INF; u=-1; for(i=0;i<n;i++) if(!visit[i] && d[i]<m) m=d[i],u=i; } return ans; } void distance(point a,point b,int i,int j) { double x=a.x-b.x; double y=a.y-b.y; len[j][i]=len[i][j]=sqrt(x*x+y*y); cost[j][i]=cost[i][j]=abs(a.h-b.h); } int main() { // freopen("in.txt","r",stdin); int n,i,j; while(scanf("%d",&n)!=EOF && n) { for(i=0; i<n; i++) scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].h); for(i=0;i<n;i++) for(j=i+1; j<n; j++) distance(p[i],p[j],i,j); double l=0.0,r=1e4,mid,ans; while(r-l>min)//采用二分法 { mid=(l+r)/2; for(i=0; i<n; i++) for(j=0; j<n; j++) mat[i][j]=cost[i][j]-mid*len[i][j]; ans=prim(0,n); if(zero(ans)) break; else if(ans<0) r=mid; else l=mid; } printf("%.3lf/n",mid); } return 0; }

你可能感兴趣的:(算法,struct,iterator,存储,Graph,distance)