Minimal Ratio Tree
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3057 Accepted Submission(s): 917
Problem Description
For a tree, which nodes and edges are all weighted, the ratio of it is calculated according to the following equation.
Given a complete graph of n nodes with all nodes and edges weighted, your task is to find a tree, which is a sub-graph of the original graph, with m nodes and whose ratio is the smallest among all the trees of m nodes in the graph.
Input
Input contains multiple test cases. The first line of each test case contains two integers n (2<=n<=15) and m (2<=m<=n), which stands for the number of nodes in the graph and the number of nodes in the minimal ratio tree.
Two zeros end the input. The next line contains n numbers which stand for the weight of each node. The following n lines contain a diagonally symmetrical n×n connectivity matrix with each element shows the weight of the edge connecting one node with another.
Of course, the diagonal will be all 0, since there is no edge connecting a node with itself.
All the weights of both nodes and edges (except for the ones on the diagonal of the matrix) are integers and in the range of [1, 100].
The figure below illustrates the first test case in sample input. Node 1 and Node 3 form the minimal ratio tree.
Output
For each test case output one line contains a sequence of the m nodes which constructs the minimal ratio tree. Nodes should be arranged in ascending order. If there are several such sequences, pick the one which has the smallest
node number; if there‘s a tie, look at the second smallest node number, etc. Please note that the nodes are numbered from 1 .
Sample Input
3 2 30 20 10 0 6 2 6 0 3 2 3 0 2 2 1 1 0 2 2 0 0 0
Sample Output
1 3 1 2
题意:在一个n个点的图中取出m个点的生成树 每个点和边都有相应的权值 当前m个点的树 根据题中所给计算公式求出当前ratio最小的那个生成树 并按照点的顺序输出当前生成树中的点
题解:因为数据量不大的缘故 可以DFS遍历所有的点 用Prim算法求出最小生成树边的和 之后带入公式中 比较大小 最后找到ratio最小的情况
因为最小值的精度问题调了一晚上 不过还好一次AC
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn = 15+11; const int inf = 0x7ffffff; int n, m; int edge[maxn][maxn], node[maxn], dist[maxn], temp[maxn], tree[maxn]; //edge-边 node-点 dist-Prim算法存放路径 temp-存放最小生成树用到的点 tree-存放最终答案的点 bool mark[maxn]; //标记 double minratio; //最小值 void dfs(int k, int num); //深搜 int prim(int u); //Prim int main(){ while(scanf("%d%d", &n, &m)!=EOF && (n&&m)){ //输入nm minratio = inf * 1.0; //最小值赋初值 for(int i = 1; i <= n; ++i){ scanf("%d", &node[i]); //输入点 } for(int i = 1; i <= n; ++i){ for(int j = 1; j <= n; ++j){ scanf("%d", &edge[i][j]); //输入边 } } for(int i = 1; i <= n; ++i){ //取出起始点并进入DFS temp[1] = i; dfs(i, 1); } for(int i = 1; i < m; ++i){ //格式化输出 printf("%d ", tree[i]); } printf("%d\n", tree[m]); } return 0; } void dfs(int k, int num){ if(num == m){ //m个点 则 进入判断 int sum = 0; //存放点的权值和 for(int i = 1; i <= m; ++i){ sum += node[temp[i]]; } double Ratio = prim(k) * 1.0 / sum; //Prim取出最小边的和 根据题目中所给的求出ratio if(Ratio - minratio<-(1e-9)){ //因为浮点数 判断大小注意精度 minratio = Ratio; for(int i = 1; i <= m; ++i){ //tree收入当前最小值的点 tree[i] = temp[i]; } } return ; } for(int i = k+1; i <= n; ++i){ //如果点的数目不足m 则 继续DFS temp[num+1] = i; dfs(i, num+1); } } int prim(int u){ for(int i = 1; i <= n; ++i){ dist[temp[i]] = edge[u][temp[i]]; //dist赋值 } memset(mark, false, sizeof(mark)); //标记赋初值 int sum = 0; mark[u] = true; //已经放入最小生成树的点 dist[u] = 0; for(int i = 1; i < m; ++i){ int point = u; for(int j = 1; j <= m; ++j){ //找到与当前点之间边的权值最小的点 if(point == u && !mark[temp[j]]){ point = temp[j]; } if(!mark[temp[j]] && dist[point] > dist[temp[j]]){ point = temp[j]; } } mark[point] = true; //将这个点收入最小生成树的集合 sum += dist[point]; //边的和 for(int j = 1; j <= m; ++j){ //更新当前最小生成树集合中的点与不在集合中的点之间边权值最小的边 if(!mark[temp[j]] && dist[temp[j]] > edge[point][temp[j]]){ dist[temp[j]] = edge[point][temp[j]]; } } } return sum; //返回最小生成树边的权值和 }