题目的大概意思是,给定n个顶点和这些顶点的邻接矩阵,求Ratio值最小(边权和/点权和)的含m个顶点的最小生成树并输出。(输出顶点时从小到大输出)
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
代码:
//
// main.cpp
// HDU2489
//
// Created by jinyu on 2018/7/15.
// Copyright © 2018年 jinyu. All rights reserved.
//
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
int N,M;
struct dingD{
int weight;
int index;
};
dingD d[20];
int arc[20][20];
int visited1[20] = {0} ;
int dist[20];
vector<int> selected;
double MINRADTIO = INF;
void Prim(vector<int> s,int sum){
double res = 0;
for(int i = 1;i<=N;i++)
{
visited1[i] = 0;
dist[i] = arc[s[0]][i];
}
visited1[s[0]] = 1;
for(int i = 1;i<M;i++){
int pos = s[0];
int min = INF;
for(int j = 0;j<M;j++){
if(!visited1[s[j]] && dist[s[j]]<min){
min = dist[s[j]];
pos = s[j];
}
}
visited1[pos] = 1;
res += min;
for(int k =0;k<M;k++){
if( dist[s[k]] > arc[pos][s[k]]){
dist[s[k]] = arc[pos][s[k]];
}
}
}
res = (double)res/sum;
if(res<MINRADTIO)
{
MINRADTIO = res;
selected = s;
}
}
void dfs(vector<int> s,int num,int sum,int index){
if(num==M){
Prim(s,sum);
return ;
}
for(int i = index+1;i<=N;i++){
s.push_back(i);
dfs(s,num+1,sum+d[i].weight,i);
s.pop_back();
}
}
int main(){
cin>>N>>M;
while(N!=0 && M!=0){
for(int i = 1;i<=N;i++){
visited1[i] = 0;
for(int j = 1;j<=N;j++){
arc[i][j] = INF;
}
}
int nn = N;
while(nn--){
int w;
cin>>w;
d[N-nn].weight = w;
}
for(int i = 1;i<=N;i++){
for(int j = 1;j<=N;j++){
cin>>arc[i][j];
}
}
vector<int> s;
dfs(s,0,0,0);
sort(selected.begin(),selected.end());
cout<<selected[0];
for(int i = 1;i<selected.size();i++){
cout<<" "<<selected[i];
}
cout<<endl;
selected.clear();
MINRADTIO = INF;
cin>>N>>M;
}
return 0;
}
刚看到题时就想直接枚举所有顶点的m阶最小生成树,但其实这样是得不到所有m阶最小生成树的,会有重复。于是想到采用dfs的方法,将它分成多个包含m个顶点的子图,并把子图顶点存到vector s中,再对每个s进行Prim。
这里第一次提交时出现了TLE,原因是在dfs中没有多想就直接用个数组来判断当前结点是否被访问,每次都去循环n次,结果导致超时。这样其实中间有会出现多次重复,需要优化一下。具体就是设置一个参数index,每次递进搜索只搜索下标比index大的结点,这样不会得到重复的集合,时间也大大优化。