题意
在n个节点中找出由m个节点组成的树,这个数的ratio最小
应为节点数量很少,最小生成树加枚举所有n个节点的情况就能过
这里用到了二进制枚举。
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <list>
#include <map>
using namespace std;
typedef long long ll;
const int MAXN = 15 + 10;
const int NIL = -1;
const int INF = 0x3f3f3f3f;
int G[MAXN][MAXN];
int res[MAXN];
int prim(int a[], int n)
{
int ans = 0;
bool vis[MAXN]{ false };
int dist[MAXN];
for (int i = 1; i < n; ++i)
dist[i] = G[a[0]][a[i]];
dist[0] = 0;
vis[0] = true;
for (int k = 0; k < n - 1; ++k) //n - 1次
{
int id = NIL;
int minicost = INF;
for (int i = 1; i < n; ++i)
{
if (vis[i])
continue;
if (dist[i] < minicost)
{
minicost = dist[i];
id = i;
}
}
if (id == NIL)
break;
ans += minicost;
vis[id] = true;
for (int i = 1; i < n; ++i)
{
if (vis[i])
continue;
dist[i] = min(dist[i], G[a[id]][a[i]]);
}
}
return ans;
}
int main(void)
{
int n, m;
int w[MAXN];
int a[MAXN];
while (scanf("%d %d", &n, &m) != EOF)
{
if (n == 0 && m == 0)
return 0;
double ans = INF;
for (int i = 0; i < n; ++i)
scanf("%d", &w[i]);
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
scanf("%d", &G[i][j]);
int N = 1 << n;
for (int i = 0; i < N; ++i)
{
int cnt = 0;
int W = 0;
for (int j = 0; j < n; ++j)
{
if (i & (1 << j))
{
a[cnt++] = j;
W += w[j];
}
}
if (cnt == m)
{
double t = prim(a, m);
t /= W;
if (ans > t)
{
ans = t;
memcpy(res, a, sizeof(a));
}
}
}
for (int i = 0; i < m; ++i)
{
if (i)
putchar(' ');
printf("%d", res[i] + 1);
}
printf("\n");
}
return 0;
}