2008 北京区域赛 Minimal Ratio Tree
/* 题目: 一个有n个节点的完全图,每条边、每个节点都有权值,在该图上求一个有m 个节点的生成树,使得该树的边权值和比点权和是所有m个节点的生成树中的 最小值,如果有多解输出字典序最小的 分析: 递归枚举m个节点,然后根据prim算法求的该比率,记录最小的边权和比点权 和最小的那棵树即可 */ #include <cstdio> #include <cstring> const int X = 17; #define INF 1e9 int map[X][X]; int wv[X],n,m; bool use[X]; int ans[X]; int mnode[X]; double cnt; int dis[X]; void prim() //prim算法求最小生成树 { memset(use,false,sizeof(use)); memset(dis,0x7f,sizeof(dis)); int MIN,k; int ret = 0; dis[1] = 0; for(int i=1;i<=m;i++) { MIN = INF; for(int j=1;j<=m;j++) if(!use[j]&&dis[j]<MIN) MIN = dis[k = j]; ret += MIN; use[k] = true; for(int j=1;j<=m;j++) if(!use[j]&&dis[j]>map[mnode[k]][mnode[j]]) dis[j] = map[mnode[k]][mnode[j]]; } int node_w = 0; for(int i=1;i<=m;i++) node_w += wv[mnode[i]]; double temp = ret*1.0/node_w; if(cnt>temp) { cnt = temp; for(int i=1;i<=m;i++) ans[i] = mnode[i]; } } void solve(int have,int cur) //递归枚举m个节点 { if(have==m+1) { prim(); return; } for(int i=cur;i<=n;i++) { mnode[have] = i; solve(have+1,i+1); } } int main() { freopen("sum.in","r",stdin); freopen("sum.out","w",stdout); while(scanf("%d%d",&n,&m),n||m) { cnt = INF; for(int i=1;i<=n;i++) scanf("%d",&wv[i]); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&map[i][j]); solve(1,1); printf("%d",ans[1]); for(int i=2;i<=m;i++) printf(" %d",ans[i]); printf("\n"); } return 0; }