题目大意:

给出一个图,要求在图中选取一些节点和边构成一个“边权和/点权和”最小的树。

思路:

用深搜从遍历所有m个点的选择方案,方案一旦确定,点权值也被确定。然后用最小生成树计算边权值。建立一个数组tree[]记录结果,将当前最小比值的选择方案存入tree[]中。

下面代码有一点需要注意,因为之前编写prim模板时将变量n定义为节点数量,而本题题干中定义m才为需要计算的节点的数量,n则为总节点数量。所以此题中m与n的定义与题干中相反。

代码:

#include<iostream>
#include<stdio.h>
using namespace std;
#define maximum 2147483647
#define datasize 15
int key[datasize+1],v[datasize+1],map[datasize+1][datasize+1],heap_size_a,length_a,sum,n;
//以上是prim使用到的变量。 
int parent(int i)
{
	return i/2;
}
int left(int i)
{
	return 2*i;
}
int right(int i)
{
	return 2*i+1;
}
void min_heapify(int a[],int i)
{
	int l=left(i),r=right(i),least;
	if(l<=heap_size_a&&a[l]<a[i])
		least=l;
	else
		least=i;
	if(r<=heap_size_a&&a[r]<a[least])
		least=r;
	if(least!=i)
	{
		swap(a[i],a[least]);
		swap(v[i],v[least]);
		min_heapify(a,least);
	}
}
void build_min_heap(int a[])
{
	heap_size_a=length_a;
	for(int i=length_a/2;i>=1;i--)
		min_heapify(a,i);
}
//以上为堆基本操作,以下为优先队列基本操作  
int heap_extract_min(int a[])
{
	int min=v[1];
	sum+=a[1];
	a[1]=a[heap_size_a];
	v[1]=v[heap_size_a];
	heap_size_a--;
	min_heapify(a,1);
	return min;
}
void heap_decrease_key(int a[],int i,int key)
//key不能大于元素i的值 
{
	a[i]=key;
	while(i>1&&a[parent(i)]>a[i])
	{
		swap(a[i],a[parent(i)]);
		swap(v[i],v[parent(i)]);
		i=parent(i);
	}
}
void prim(int r)
{
	for(int i=1;i<=datasize;i++)
	{
		key[i]=maximum;
		v[i]=i;
	}
	key[r]=0;
	length_a=n;
	build_min_heap(key);
	while(heap_size_a>=1)
	{
		int u=heap_extract_min(key);
		for(int i=1;i<=heap_size_a;i++)
			if(map[u][v[i]]<key[i])
				heap_decrease_key(key,i,map[u][v[i]]);
	}
}
int original[datasize+1][datasize+1],val[datasize+1],m;
//original[][]存储由输入给出的边权矩阵。
//val[]存储点权值。
//m表示图全部点的个数。 
void intput()
//输入 
{
	for(int i=1;i<=m;i++)
		scanf("%d",&val[i]);
	for(int i=1;i<=m;i++)
		for(int j=1;j<=m;j++)
			scanf("%d",&original[i][j]);
}
int tree[datasize+1];
//存储答案 
double ratio;
//ratio当前最小比值。 
bool dfs(int start,int order,int total)
//从start开始遍历,第order个节点,点权值和为total。 
{
	if(order==n+1)
	{
		sum=0;
		//初始化边权和。 
		prim(1);
		//调用prim()计算边权和。 
		if(ratio>double(sum)/double(total))
		//若当前方案中的比值小于最小比值。 
		{
			ratio=double(sum)/double(total);
			return true;
		}
		return false;
	}
	bool check=false;
	//check用于判断当前以order个点为基础延伸出的方案中是否有比值小于ratio的方案。 
	for(int i=start,res;i<=m-(n-order);i++)
	{
		map[order][0]=i;
		for(int j=1;j<=order;j++)
		//利用original[][]构建order个节点的新邻接矩阵map[][]。 
			map[order][j]=map[j][order]=original[i][map[j][0]];
		if(dfs(i+1,order+1,total+val[i]))
		//若以当前order个节点为基础延伸出的方案中有比值小于ratio的方案。 
		{
			tree[order]=i;
			//用tree[]记录下更优方案中的第order个节点i。 
			check=true;
		}
	}
	return check;
}
int main()
{
	while(scanf("%d%d",&m,&n)&&(m||n))
	{
		intput();
		ratio=100;
		//初始化ratio为一个不可能达到的最大值。(n*100)/(n*1-1)趋近于100。 
		dfs(1,1,0);
		for(int i=1;i<=n;i++)
		//输出结果。 
			if(i-n)
				printf("%d ",tree[i]);
			else
				printf("%d\n",tree[i]);
	}
}