For a tree, which nodes and edges are all weighted, the ratio of it is calculated according to the following equation.
Given a complete graph of n nodes with all nodes and edges weighted, your task is to find a tree, which is a sub-graph of the original graph, with m nodes and whose ratio is the smallest among all the trees of m nodes in the graph.
InputInput contains multiple test cases. The first line of each test case contains two integers n (2<=n<=15) and m (2<=m<=n), which stands for the number of nodes in the graph and the number of nodes in the minimal ratio tree. Two zeros end the input. The next line contains n numbers which stand for the weight of each node. The following n lines contain a diagonally symmetrical n×n connectivity matrix with each element shows the weight of the edge connecting one node with another. Of course, the diagonal will be all 0, since there is no edge connecting a node with itself.
Given a complete graph of n nodes with all nodes and edges weighted, your task is to find a tree, which is a sub-graph of the original graph, with m nodes and whose ratio is the smallest among all the trees of m nodes in the graph.
All the weights of both nodes and edges (except for the ones on the diagonal of the matrix) are integers and in the range of [1, 100].
The figure below illustrates the first test case in sample input. Node 1 and Node 3 form the minimal ratio tree.
OutputFor each test case output one line contains a sequence of the m nodes which constructs the minimal ratio tree. Nodes should be arranged in ascending order. If there are several such sequences, pick the one which has the smallest node number; if there's a tie, look at the second smallest node number, etc. Please note that the nodes are numbered from 1 .Sample Input
3 2 30 20 10 0 6 2 6 0 3 2 3 0 2 2 1 1 0 2 2 0 0 0Sample Output
1 3
1 2
思路: 要求从n中选m个组成的一颗最小生成树(也就是边权最小sum),并且要在此条件下点权最小,也就是sum/ans最小,那么可以暴力枚举出从n个点在选m个,然后取sum/ans最小的那个;
思路可以参见:https://blog.csdn.net/xingyeyongheng/article/details/9373271
组合选数参见:https://blog.csdn.net/qq_34649947/article/details/80408580
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int maxn = 100 + 10;
const double eps = 1e-8;
int cost[maxn][maxn];//表示边权值,最小生成树算法用到
int mincost[maxn];
bool used[maxn];
int V;//顶点数 == m
int n, m;
int weight[maxn];//每个点的权值
int record[maxn];//每次选取的m个点的集合
int result[maxn];//最后的结果集
int prim() {
//注意!!!这里的i-v的关系,prim的集合应该是record里面的,不是i下标
memset(used, false, sizeof(used));
for (int i = 1; i <= m; i++) {
int v = record[i];
mincost[v] = INF;
used[v] = false;
}
mincost[record[1]] = 0;
int res = 0;
while (true) {
int u = -1;
//从不属于最小生成树集合X的顶点中选取从当前已经生成的树X到其权值最小的顶点
for (int i = 1; i <= m; i++) {
if (!used[record[i]] && (u == -1 || mincost[record[i]] < mincost[u])) u = record[i];
}
if (u == -1) break;
used[u] = true;//把顶点加入到X
res += mincost[u];//把边的长度加入到结果里
//更新当前u的领边的情况
for (int i = 1; i <= m; i++) {
int v = record[i];
mincost[v] = min(mincost[v], cost[u][v]);
}
}
return res;
}
double minratio = INF*1.0;//
void dfs(int k, int dp) {
if (dp == m) {
int ans = 0;
// for (int i = 1; i <= m; i++) {
// cout << record[i] << " ";
// }
for (int i = 1; i <= m; i++) ans += weight[record[i]];
int a = prim();
// cout << a << " " << ans << endl;
double sum = a*1.0/ans;
// cout << sum << endl;
if (sum < minratio) {
minratio = sum;
for (int i = 1; i <= m; i++) result[i] = record[i];
}
return;
}
//若剩余的点的个数(n-k)加上目前选取的个数num小于m的话,说明即使接下来n-k个点都选取,也选不足m个点,直接return
if (n - k + dp < m) return;
for (int i = k + 1; i <= n; i++) {
record[dp + 1] = i;
dfs(i, dp + 1);
}
}
int main() {
while (~scanf("%d %d", &n, &m)) {
if (n == 0 && m == 0) break;
// V = m;//最小生成树的点的个数
//输入每个点的权值
//!!!!!!!不要忘记清空初始化
memset(result, 0, sizeof(result));
memset(weight, 0, sizeof(weight));
memset(cost, INF, sizeof(cost));
memset(record, 0, sizeof(record));
minratio = INF*1.0;
for (int i = 1; i <= n; i++) {
scanf("%d", &weight[i]);
}
//输入边权
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
scanf("%d", &cost[i][j]);
}
}
//n中选取m个点
for (int i = 1; i <= n; i++) {
record[1] = i;
dfs(i, 1);
}
for (int i = 1; i <= m; i++) {
if (i == 1) printf("%d", result[i]);
else printf(" %d", result[i]);
}
printf("\n");
}
return 0;
}