POJ 3925 Minimal Ratio Tree

来源:互联网 发布:燃情岁月知乎 编辑:程序博客网 时间:2024/05/17 03:30

题目大意:

        定义一棵树的比率:即所有边权值之和比上所有点权值之和。

        现有一个完全图(各点之间连通)共有n个点(2 ≤ n ≤ 15),现要出其具有m个点(2 ≤ m ≤ n)的子树(同时也是子图),其比率是所有子树中比率最小的。

        现有多个测例,每个测例中给出n和m(输入以n = 0结束),接着给出每个点的权值,再接着给出一个n × n的边权值矩阵,所有权值的范围为[1, 100],并且点是从1开始编号的,求其最优比例生成树,要求按照从小到大顺序打印该树的点序号,如果有多个解则只输出字典序最小的那个解。

题目链接

注释代码:

/*                                             * Problem ID : POJ 3925 Minimal Ratio Tree  * Author     : Lirx.t.Una                                             * Language   : C++                            * Run Time   : 16 ms                                             * Run Memory : 148 KB                                            */#include <memory.h>#include <stdio.h>#include <math.h>//思路:指定树的结点数后,由于是完全图(任意两点之间有通路)//所以任意m个点都可以组成一个生成树//不妨先对m个点进行枚举,比如枚举出了一组点,那么这组点的点权值就固定了//如果想让其 边权/点权 最小就必须得让边权最小,那么就得求最小生成树//因此总问题就转化成了求所有m个点的最小生成树中比例最小的了//代码中的select组合函数可以使得求出的m个点的组合按照点的编号从小到大排列#defineTRUE1#defineFALSE0#defineINF101//float INF//浮点数的无穷大,越大越好吧#defineFINF1E10#defineESP1E-8//最大点数#defineMAXN16#defineMIN(x,y)( (x) < (y) ? (x) : (y) )typedefcharBOOL;inttree[MAXN];//保存最小生成树中结点的编号,编号从1开始计intans[MAXN];//保存最终结果的最小生成树intwn[MAXN];//weight of node,各结点的权值intg[MAXN][MAXN];//graph,保存图的任意两点之间的距离intdist[MAXN];//Prim算法中当前最短距离BOOLvist[MAXN];//Prim算法中判断一个点是否被访问过doublemrtio;//minimum ratio,保存当前最小的比率(即最小生成树的边权/点权)voidprim(int m) {//m为最小生成树结点数inti;intmin_dist;intmin_node;inttot_we;//totoal weight of edge,边权总和inttot_wn;//total weight of node,点权总和intnvist;//number of visited node,被访问过的点数doublertio;//ratio,比例,临时变量//正常的Prim算法memset(vist, FALSE, sizeof(vist));vist[1] = TRUE;nvist   = 1;dist[1] = 0;for ( i = 2; i <= m; i++ ) dist[i] = g[ tree[1] ][ tree[i] ];for ( tot_wn = 0, i = 1; i <= m; i++ )tot_wn += wn[ tree[i] ];tot_we = 0;while ( nvist < m ) {for ( min_dist = INF,  i = 2; i <= m; i++ )if ( !vist[i] && dist[i] < min_dist ) {min_dist = dist[i];min_node = i;}vist[min_node] = TRUE;tot_we += dist[min_node];nvist++;for ( i = 2; i <= m; i++ )if ( !vist[i] )dist[i] = MIN( dist[i], g[ tree[min_node] ][ tree[i] ] );}//Prim算法结束//判断当前最小生成树是否为比例最优的rtio = (double)tot_we / (double)tot_wn;if ( fabs( rtio - mrtio ) > ESP && rtio < mrtio ) {//注意!!一定不能少fabs( rtio - mrtio ) > ESP//因为有可能出现相等的情况,题目中要求相等情况中取字典序最小的//前面的组合一定比当前组合字典序小!!!memcpy(ans, tree, sizeof(ans));mrtio = rtio;}}voidselect( int bg, int nd, int n, int m ) {//求m点的所有组合//begin,其实筛选位置//done number,当前已经筛选出来的结点数//n为总点数//m为最小生成树的点数if ( nd == m ) {//如果已经全部筛选完,则可以直接调用Prim算法求比率prim(m);return ;}inti;for ( i = bg; i <= n; i++ ) {//否则就从begin开始筛选tree[nd + 1] = i;select( i + 1, nd + 1, n, m );//注意!!一定要从i + 1位置开始筛选//这样可以保证筛选出来的序列是按照编号升序的!!!}}intmain() {intn, m;//图结点数和最小生成树结点数inti, j;//计数变量while ( scanf("%d%d", &n, &m), n ) {for ( i = 1; i <= n; i++ ) scanf("%d", wn + i);for ( i = 1; i <= n; i++ )for ( j = 1; j <= n; j++ )scanf("%d", &g[i][j]);mrtio = FINF;select( 1, 0, n, m );printf("%d", ans[1]);for ( i = 2; i <= m; i++ ) printf(" %d", ans[i]);putchar('\n');}return 0;}

无注释代码:

#include <memory.h>#include <stdio.h>#include <math.h>#defineTRUE1#defineFALSE0#defineINF101#defineFINF1E10#defineESP1E-8#defineMAXN16#defineMIN(x,y)( (x) < (y) ? (x) : (y) )typedefcharBOOL;inttree[MAXN];intans[MAXN];intwn[MAXN];intg[MAXN][MAXN];intdist[MAXN];BOOLvist[MAXN];doublemrtio;voidprim(int m) {inti;intmin_dist;intmin_node;inttot_we;inttot_wn;intnvist;doublertio;memset(vist, FALSE, sizeof(vist));vist[1] = TRUE;nvist   = 1;dist[1] = 0;for ( i = 2; i <= m; i++ ) dist[i] = g[ tree[1] ][ tree[i] ];for ( tot_wn = 0, i = 1; i <= m; i++ )tot_wn += wn[ tree[i] ];tot_we = 0;while ( nvist < m ) {for ( min_dist = INF,  i = 2; i <= m; i++ )if ( !vist[i] && dist[i] < min_dist ) {min_dist = dist[i];min_node = i;}vist[min_node] = TRUE;tot_we += dist[min_node];nvist++;for ( i = 2; i <= m; i++ )if ( !vist[i] )dist[i] = MIN( dist[i], g[ tree[min_node] ][ tree[i] ] );}rtio = (double)tot_we / (double)tot_wn;if ( fabs( rtio - mrtio ) > ESP && rtio < mrtio ) {memcpy(ans, tree, sizeof(ans));mrtio = rtio;}}voidselect( int bg, int nd, int n, int m ) {if ( nd == m ) {prim(m);return ;}inti;for ( i = bg; i <= n; i++ ) {tree[nd + 1] = i;select( i + 1, nd + 1, n, m );}}intmain() {intn, m;inti, j;while ( scanf("%d%d", &n, &m), n ) {for ( i = 1; i <= n; i++ ) scanf("%d", wn + i);for ( i = 1; i <= n; i++ )for ( j = 1; j <= n; j++ )scanf("%d", &g[i][j]);mrtio = FINF;select( 1, 0, n, m );printf("%d", ans[1]);for ( i = 2; i <= m; i++ ) printf(" %d", ans[i]);putchar('\n');}return 0;}

单词解释:

minimal:adj, 最小的

weight:n, 权值; vt, 赋予权值

equation:n, 方程式,等式

diagonal:n, 对角线,斜线; adj, 对角线的,斜线的

symmetrical:adj, 对称的

connectivity:n, 连通性(数学术语)

tie:n, 平局

ratio:n, 比率,比例

0 0
原创粉丝点击