这是题目:
6:地震之后
总时间限制: 1000ms 内存限制: 130000kB
描述
2008年地震之后,坚强县受灾严重,该县通信线路也收到了致命的打击,县总部为了能够及时的向各村的发送消息,命令小强去解决一下这个问题。
小强经过调查发现,为了能够快速的实现通信,当务之急是能够建立起一条从总部可以向各个村单向发送信息的通信系统。由于灾后情况紧急,不是每一个村之间都能够快速的建立起通信线路,只有有限的村比如村A和村B间可以建立单向的从A向B发送信息的通信线路,小强现在的问题就是要找出一个合适的方案,使从总部发送出的信息能够通到各村,而用的总的通信线路是最短的(毕竟情况危急,物质短缺)。
输入
输入包括多组的测试数据。
对于每一组测试数据:
1、第一行包括两个整数N(1<=N<=100),M(M<<=10000),N表示通信网络中村子的数量,M表示在这些村子中,可以有M条通信线路建起来。
2、接下来N行,每行两个整数xi,yi,代表着这个村子的X轴,Y轴的坐标(xi,yi)
3、接下来M行,每行两个整数c1(1<=c1<=N),c2(1<=c2<=N),代表着从村c1到村c2可以建一个单向线路。
注:两村之间的直线段距离(通信线路长度),即为两点间的欧式距离。
县总部所在的村假设都在编号为1的村。
输入以文件终止为结束。
输出
对于每一组的测试数据,输出完全的一行,值为最短的总长度,结果保留两位小数。
如果不能建立一个单向的临时网络,输出"NO".
样例输入
4 6
3 6
4 6
3 4
7 20
1 2
1 3
2 3
3 4
3 1
3 2
4 3
0 0
1 0
1 1
1 2
1 3
4 1
2 3
样例输出
19.49
NO
裸的最小树形图/有向图的最小生成树。详细分析见: 最小树形图,朱刘算法。虽然是模板,但还是再一次默写了一遍,再一次注释了一遍。
代码清单:
#include <iostream> #include <math.h> #include <cstdio> using namespace std; #define N 105 #define INFINITE 999999999 #define MYTYPE double struct _point { int x; int y; } point[N]; struct _edge { int from; int to; MYTYPE cost; } edge[N*N]; MYTYPE inw[N]; //最小入边 int vis[N]; //是否被访问 int id[N]; //由当前图到重构图的映射 int pre[N]; //前驱顶点 MYTYPE Directed_MST(int root, int NV, int NE) { MYTYPE ret=0; while (1) //开始迭代过程 { //1.确定最小入边集 for(int i=0; i<NV; ++i) inw[i]=INFINITE; for (int i=0; i<NE; ++i) { int from=edge[i].from; int to=edge[i].to; if (edge[i].cost<inw[to] && from!=to) //from!=to忽略自环 { inw[to]=edge[i].cost; pre[to]=from; } } //检查是否有不可达点 for (int i=0; i<NV; ++i) { if(i==root) continue; //除根之外 if(inw[i]==INFINITE) return -1; //有不可达顶点,不可能生成最小树形图,退出 } //2.找环 for (int i=0; i<NV; ++i) { vis[i]=-1; id[i]=-1; } int newidx=0; inw[root]=0; for (int i=0; i<NV; ++i) //有两个作用:计算最小入边集的权值和;检查是否有环,如果有,重新对点进行编号 { ret+=inw[i]; int v=i; while (vis[v]!=i && id[v]==-1 && v!=root) //由v回溯。能回到根,即最后v==root,那么肯定不在环里;回不到根,v!=root,v有可能在环里,也有可能不在(回溯到一个环然后出不去了,同样也到不了根)。 //若v在环里,则环上所有点的id[]值会被重新标号,不再是-1;若是后一种情况,它前驱的环上的点的id[]已被修改为非-1,不能通过“id[v]==-1”这个条件的检查。 { vis[v]=i; v=pre[v]; } if (v!=root && id[v]==-1) //两个条件保证了:1.在环上2.这环没被处理过 { //下面把环上所有的点的标号设置为同一个 for (int u=pre[v]; u!=v; u=pre[u]) { id[u]=newidx; } id[v]=newidx; ++newidx; } } if(newidx==0) break; //无环,ret就是答案,跳出迭代 for (int i=0; i<NV; ++i) { if(id[i]==-1) id[i]=newidx++; //给环外的点继续编号 } //3.重新构图,准备下一次迭代 for (int i=0; i<NE; ++i) { int to=edge[i].to; edge[i].from=id[edge[i].from]; edge[i].to=id[edge[i].to]; if(edge[i].from!=edge[i].to) { edge[i].cost -= inw[to]; //算法的关键 } } //为下一轮迭代赋初值 NV=newidx; root=id[root]; } return ret; } MYTYPE calcdist(int point_a, int point_b) { MYTYPE delta_x=(MYTYPE)(point[point_a].x - point[point_b].x); MYTYPE delta_y=(MYTYPE)(point[point_a].y - point[point_b].y); return sqrt(delta_x*delta_x + delta_y*delta_y); } int main() { //freopen("D:\\in.txt", "r", stdin); //freopen("D:\\out.txt", "w", stdout); int n, m, x, y, from, to; MYTYPE ans; while (scanf("%d", &n) != EOF) { scanf("%d", &m); for (int i=0; i<n; ++i) //n个顶点 { scanf("%d%d", &x, &y); point[i].x=x; point[i].y=y; } for (int i=0; i<m; ++i) //m个边 { scanf("%d%d", &from, &to); edge[i].from=from-1; edge[i].to=to-1; edge[i].cost=calcdist(from-1, to-1); } ans=Directed_MST(0, n, m); if (ans==-1) printf("NO\n"); else printf("%.2f\n", ans); } return 0; }