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

题目分析:
题目求得是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;
}