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.
Input 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.
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.
For 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 .
一棵树的比率定义为“若干条边的权重之和”除以“这些条边之间点的权重之和”。现给出一个 n × n n\times n n×n的图,要求求出一个生成树使其点有m个并且其比率最小。从大到小输出这棵树的结点编号。
由于n的范围是2到15所以枚举出所有m个结点的树也不会超时。因此利用DFS枚举出所有的情况,并对每一种情况使用Prim算法求出其最小生成树,保存其最小比率树的结点,最后输出出来。
#include
#include
#include
#include
#define inf 0x3f3f3f
using namespace std;
int graph[16][16];
int nodeWeight[16]; // 结点权重数组
int dist[16]; // 结点到已确定集合的距离
bool mark[16];
int n, m;
vector<int> v, min_v;
vector<vector<int>> V;
void init(vector<int>); // 初始化距离数组函数
void Prim(); // Prim算法,参数为循环的次数
double radio(vector<int>); // 计算比率
// 深度优先搜索
// 第一个参数表示目前查看的元素下标
// 第二个参数表示目前选中的元素数量
void DFS(int, int);
int main()
{
int i, j;
double min_radio, temp;
while (cin >> n >> m) {
if (!n) {
break;
}
for (i = 1; i <= n; i++) {
cin >> nodeWeight[i];
}
for (i = 1; i <= n; i++) {
for (j = 1; j <= n; j++) {
cin >> graph[i][j];
}
}
// 使用深度深度优先搜索枚举出所有符合要求的情况
DFS(1, 0);
// 每一种情况计算一次最小生成树
for (i = 0; i < V.size(); i++) {
init(V[i]);
Prim();
for (j = 1; j <= n; j++) {
temp = radio(V[i]);
if (!i) {
min_radio = temp;
min_v = V[i];
}
else {
if (temp < min_radio) {
min_radio = temp;
min_v = V[i];
}
}
}
}
for (i = 0; i < m; i++) {
if (i) {
cout << " ";
}
cout << min_v[i];
}
cout << endl;
V.clear();
}
}
void init(vector<int> v) {
// 先把所有距离数组清零
memset(dist, 0, sizeof(dist));
// 先把所有标记数组标记为已确定
memset(mark, 1, sizeof(mark));
// 假设第一个点为首先确定的点,所以从数组的第二个开始循环
for (int i = 1; i < m; i++) {
dist[v[i]] = graph[v[0]][v[i]];
mark[v[i]] = false;
}
}
void Prim() {
int i, j, temp, pos;
for (i = 1; i < m; i++) {
temp = inf;
// 找出当前距离数组中最小的一项
for (j = 1; j <= n; j++) {
if (!mark[j] && dist[j] < temp) {
temp = dist[j];
pos = j;
}
}
mark[pos] = true;
for (j = 1; j <= n; j++) {
if (!mark[j]) {
// 更新距离数组
dist[j] = min(dist[j], graph[pos][j]);
}
}
}
}
double radio(vector<int> v) {
int i, numer = 0, denom = 0;
for (i = 0; i < m; i++) {
numer += dist[v[i]];
denom += nodeWeight[v[i]];
}
return double(numer) / denom;
}
void DFS(int num, int count)
{
if (num > n) {
// 如果满足结点的个数要求则把此情况保存起来
if (count == m) {
for (int i = 1; i <= n; i++) {
if (mark[i]) {
v.push_back(i);
}
}
V.push_back(v);
v.clear();
}
}
else {
mark[num] = true;
DFS(num + 1, count + 1);
mark[num] = false;
DFS(num + 1, count);
}
}
发现问题欢迎指出和纠正,谢谢!