题目问的就是,从n个点中选m个点,生成树值和点的权值比例最小,用二进制来枚举全部情况。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=17;
const int MAXN=1<<N;
const double inf=1e10;
int g[N][N],dist[N],vis[N],use[MAXN],n,m,pe[N];
int get(int k)
{
int ret=0;
while(k)
{
ret+=k%2;
k/=2;
}
return ret;
}
double prim(int k)
{
int sum_n,sum_m,i,j,u,w;
memset(vis,0,sizeof(vis));
for(i=0; i<n; i++)
if(k&(1<<i))
{
u=i;
break;
}
sum_m=0;
sum_n=pe[u];
for(i=0; i<n; i++)
{
if((1<<i)&k)
dist[i]=g[u][i];
else
dist[i]=1024;
//printf(" %d",dist[i]);
}
//printf("\n");
vis[u]=1;
int cnt=get(k);
if(cnt!=m) return inf;
//printf(" %d %d %d\n",k,u,cnt);
for(i=2; i<=cnt; i++)
{
//if(k==11) printf("%d %d\n",i,cnt);
u=-1;
w=1024;
for(j=0; j<n; j++)
{
if(vis[j]==1) continue;
if(((1<<j)&k)==0) continue;
//printf("%d\n",dist[j]);
if(dist[j]<w)
{
u=j;
w=dist[j];
}
}
//if(k==11) printf("%d %d %d\n",cnt,i,u);
if(u==-1) return inf;
sum_m+=w;
sum_n+=pe[u];
//if(k==11) printf("%d %d\n",sum_m,sum_n);
vis[u]=1;
for(j=0; j<n; j++)
{
if(vis[j]==1) continue;
if(((1<<j)&k)==0) continue;
dist[j]=min(dist[j],g[u][j]);
}
}
//printf("%d %d %d %.2f\n",k,sum_n,sum_m,sum_m*1.0/sum_n);
return sum_m*1.0/sum_n;
}
int main()
{
int i,j,u,v,maxn,st;
double ans;
while(~scanf("%d%d",&n,&m))
{
if(n==0&&m==0) break;
for(i=0; i<n; i++)
scanf("%d",&pe[i]);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
scanf("%d",&g[i][j]);
}
}
maxn=1<<n;
memset(use,0,sizeof(use));
ans=inf;
for(i=1; i<maxn; i++)
{
double tp=prim(i);
//printf("%.2f %.2f\n",ans,tp);
if(tp-ans<-(1e-8))
{
ans=tp;
st=i;
//printf(" %f %d\n",ans,st);
}
use[i]=1;
}
int flag=0;
//printf("%d\n",st);
for(i=0; i<n; i++)
{
if(((1<<i)&st)==0) continue;
if(flag) printf(" ");
else flag++;
printf("%d",i+1);
}
printf("\n");
}
return 0;
}