POJ 3925 Minimal Ratio Tree 最小生成树

题目大意:

给定N个点(2<=N<=15)的图,求在顶点数满足M个的子图中,满足如下式子的最小值。

Ratio=\frac{\sum{edgeweight}}{\sum{nodeweight}}



题目分析:

题目求得是Ratio的最小值,Ratio=子图中的边权和/点权和 ,考虑在某一满足条件的子图中,∑nodeweight是定值,则在子图中求Ratio的最小值就是子图的最小生成树。题目的数据比较小,对于N<=15,共有2^15次方种可能子图,每次求Prim复杂度为O(M^2),是可以接受的。

其中比较关键的有两点:

1:求Ratio(上文已讲过)

2:枚举子图,类似枚举子图问题我们可以用dfs回溯完成。

      如下是比较关键的几行代码

      dfs(int dep,int pos)

      {

             ********

           use[dep]=pos;

           dfs(dep+1,pos+1)//枚举到pos位置的下一个

           //这样我们可以保证子图不重复;

            dfs(dep,pos+1);//在当前的dep下更改成pos+1

      }

如下代码(附带注释):

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 16
#define INF 1000000
using namespace std;
int n,m,node[N],edge[N][N],fin[N],d[N],use[N];
double ans;
double Prim()
{
	int s,pos,res,cnt=0;
	for(int i=0;i<m;i++) 
	   d[use[i]]=INF;
	s=use[0];
	for(int i=1;i<=m-1;i++)
	{
		res=INF;
		d[s]=-1;
		for(int j=0;j<m;j++)
		{
			if(s!=use[j]&&d[use[j]]>0)
			{
				d[use[j]]=min(d[use[j]],edge[s][use[j]]);
				if(d[use[j]]<res)
				{
					res=d[use[j]];
					pos=use[j];	
				}	
			}
		}
		s=pos;
		cnt+=res;	
	}
	res=0;
	for(int i=0;i<=m-1;i++)
		res+=node[use[i]];
	return 1.0*cnt/res;	
}
void dfs(int dep,int pos)
{
	if(dep>m||pos>n+1)//注意是n+1 
		return;
	if(dep==m)
	{
		double tem=Prim();
		if(tem<ans)
		{
			ans=tem;
			for(int i=0;i<m;i++)
			   fin[i]=use[i];
			return ;
		}
	}
	use[dep]=pos;//重建点 
	dfs(dep+1,pos+1);
	dfs(dep,pos+1);
}
int main()
{
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		if(!n&&!m)	
			break;
		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]);
		ans=INF;
		dfs(0,1);//从0,1开始搜索 
		for(int i=0;i<m;i++)
		    printf("%d%s",fin[i],i==m-1?"\n":" ");	
	}	
//	while(1);
	return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值