POJ 3925 Minimal Ratio Tree

题目大意:

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

        现有一个完全图(各点之间连通)共有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个点的组合按照点的编号从小到大排列

#define	TRUE		1
#define	FALSE		0

#define	INF			101
//float INF
//浮点数的无穷大,越大越好吧
#define	FINF		1E10
#define	ESP			1E-8

//最大点数
#define	MAXN		16

#define	MIN(x,y)	( (x) < (y) ? (x) : (y) )

typedef	char	BOOL;

int		tree[MAXN];//保存最小生成树中结点的编号,编号从1开始计
int		ans[MAXN];//保存最终结果的最小生成树

int		wn[MAXN];//weight of node,各结点的权值
int		g[MAXN][MAXN];//graph,保存图的任意两点之间的距离
int		dist[MAXN];//Prim算法中当前最短距离

BOOL	vist[MAXN];//Prim算法中判断一个点是否被访问过

double	mrtio;//minimum ratio,保存当前最小的比率(即最小生成树的边权/点权)

void
prim(int m) {//m为最小生成树结点数

	int		i;

	int		min_dist;
	int		min_node;

	int		tot_we;//totoal weight of edge,边权总和
	int		tot_wn;//total weight of node,点权总和

	int		nvist;//number of visited node,被访问过的点数

	double	rtio;//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;
	}
}

void
select( int bg, int nd, int n, int m ) {//求m点的所有组合
	//begin,其实筛选位置
	//done number,当前已经筛选出来的结点数
	//n为总点数
	//m为最小生成树的点数

	if ( nd == m ) {//如果已经全部筛选完,则可以直接调用Prim算法求比率
	
		prim(m);
		return ;
	}

	int		i;

	for ( i = bg; i <= n; i++ ) {//否则就从begin开始筛选
	
		tree[nd + 1] = i;
		select( i + 1, nd + 1, n, m );//注意!!一定要从i + 1位置开始筛选
		//这样可以保证筛选出来的序列是按照编号升序的!!!
	}
}

int
main() {

	int		n, m;//图结点数和最小生成树结点数

	int		i, 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>

#define	TRUE		1
#define	FALSE		0

#define	INF			101
#define	FINF		1E10
#define	ESP			1E-8

#define	MAXN		16

#define	MIN(x,y)	( (x) < (y) ? (x) : (y) )

typedef	char	BOOL;

int		tree[MAXN];
int		ans[MAXN];

int		wn[MAXN];
int		g[MAXN][MAXN];
int		dist[MAXN];

BOOL	vist[MAXN];

double	mrtio;

void
prim(int m) {

	int		i;

	int		min_dist;
	int		min_node;

	int		tot_we;
	int		tot_wn;

	int		nvist;

	double	rtio;

	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;
	}
}

void
select( int bg, int nd, int n, int m ) {

	if ( nd == m ) {
	
		prim(m);
		return ;
	}

	int		i;

	for ( i = bg; i <= n; i++ ) {
	
		tree[nd + 1] = i;
		select( i + 1, nd + 1, n, m );
	}
}

int
main() {

	int		n, m;

	int		i, 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值