我最开始想的是用边除以该边连接的两个顶点的值作为此边的权值,然后再用Kruskal求最小生成树。。。。。。。但这样似乎有漏洞,而且总是TLE。。。。后来上网看了题解:先递归深搜找出n个点中取m个顶点的组合,再求出其最小生成树,保留最小值即可。。。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<float.h>
#define Inf 0x7fffffff
int n,m,no[20],ed[20][20];
double mi;
int que[20],vis[20],dis[20],v[20];
void Prime() //用prime算法求m个点的最小生成树
{
int i,j,k,sum=0,w=0,min,tmp;
memset(vis,0,sizeof(vis));
for(i=1;i<=m;i++)
dis[v[i]]=Inf;
for(i=1;i<=m;i++) //初始化
dis[v[i]]=ed[v[1]][v[i]];
vis[v[1]]=1;
for(i=1;i<m;i++){
min=Inf;
tmp=-1;
for(j=1;j<=m;j++) //找最小边
if((vis[v[j]]==0)&&(dis[v[j]]<min)){
min=dis[v[j]];
tmp=v[j];
}
vis[tmp]=1;
sum+=min;
for(j=1;j<=m;j++){ //更新集合外的点与集合内点的最短距离
if((vis[v[j]]==0)&&(ed[tmp][v[j]]<dis[v[j]]))
dis[v[j]]=ed[tmp][v[j]];
}
}
for(j=1;j<=m;j++)
w+=no[v[j]];
double t=(double)sum*1.0/(double)w;
if(t < mi){ //一定要注意t和mi的数据类型
for(j=1;j<=m;j++) //将最小ratio tree的组合存入que[m]中
que[j]=v[j];
mi=t;
}
}
void dfs(int x,int c) //深搜递归,找出所有n个点中取m个点的组合
{ //并把找出的点存入v[m]
int i;
v[c]=x;
if(c==m){
Prime();
return;
}
for(i=x+1;i<=n;i++)
dfs(i,c+1);
}
int main()
{
int i,j;
while(scanf("%d %d",&n,&m)&& !(n==0&&m==0)){
for(i=1;i<=n;i++) scanf("%d",&no[i]);
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
scanf("%d",&ed[i][j]);
mi=DBL_MAX;
for(i=1;i<=n;i++)
dfs(i,1);
for(i=1;i<m;i++)
printf("%d ",que[i]);
printf("%d\n",que[m]);
}
system("pause");
return 0;
}