Minimal Ratio Tree
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 5412 Accepted Submission(s): 1770
Problem Description
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
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.
Output
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 .
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 0
Sample Output
1 3 1 2
题意:给出一个图,有n个点,然后给出边的权值,输出任意选m个点之后组成的树的比例值(比例值=边的权值和 / 点的值和)最小的树的点 ,输入0 0结束
思路:就是先用dfs或者状压dp暴力选出m个点,然后用最小生成树来计算边的权值和,要使比例值最小,边的权值和就要相对于的小。。。比如找到了m个点,我们要计算边的权值和最小才能让组成的树的比例值变小。一开始我不知道为什么要用最小生成树,后面才知道。。。
坑点:看其他dalao的博客好像是dfs m个点的时候会超时,到时候我们注意下就行了
AC代码:
#include <iostream> #include <cstring> #include <algorithm> using namespace std; const int maxn=20; const double INF=999999999; double map[maxn][maxn],val[maxn],dis[maxn]; //val用来记录点的值 int sum[maxn],ans[maxn],n,m; //sum是找到的m个点,ans是要找的最小比例值的m个点 bool vis[maxn],flag; double Min; //Min用来判断比例值的 double prim() { bool vis_p[maxn]; memset(vis_p,false,sizeof(vis_p)); for (int i=1;i<=m;i++) dis[i]=INF; dis[1]=0; for (int i=1;i<=m;i++) { double MIN=INF;int flag; for (int j=1;j<=m;j++) if (vis_p[j]==true) continue; else if (MIN>dis[j]) { MIN=dis[j]; flag=j; } if (MIN==INF) break; vis_p[flag]=true; // cout<<MIN<<" -- "<<flag<<endl; // cout<<MIN<<" -- "<<flag<<" "; for (int j=1;j<=m;j++) if (vis_p[j]==true) continue; else dis[j]=min(dis[j],map[sum[flag]][sum[j]]); } // cout<<endl; double s=0; for (int i=1;i<=m;i++) s=s+dis[i]; return s; } void dfs(int num,int step) //一开始 num==1,后面for 循环的时候我们的i从Num开始,因为如果不从num开始我们会有这种情况 { // sum里面是1 2 3 之后又会出现 1 3 2,这两种情况是一样的,避免重复 if (step==m+1) //如果找到m个点就计算点的值和 和 边的权值和 { sort(sum+1,sum+step); double sum1=prim(); double sum2=0; for (int i=1;i<step;i++) sum2=sum2+val[sum[i]]; // cout<<Min<<"???"<<(sum1/sum2)<<endl; if (Min>(sum1/sum2)) { Min=sum1/sum2; for (int i=1;i<step;i++) //记录最小比例值的树的点 ans[i]=sum[i]; // cout<<"OK"<<endl; } // for (int i=1;i<step;i++) // if (i==step-1) cout<<sum[i]<<endl; // else cout<<sum[i]<<"-"; // cout<<sum1<<"---"<<sum2<<endl; return ; } for (int i=num;i<=n;i++) if (vis[i]==true) continue; else { // cout<<step<<"------"<<i<<endl; vis[i]=true; sum[step]=i; dfs(i,step+1); vis[i]=false; } } int main() { int i,j; while (cin>>n>>m&&n&&m) { memset(val,0,sizeof(val)); for (i=1;i<=n;i++) scanf("%lf",&val[i]); for (i=1;i<=n;i++) for (j=1;j<=n;j++) scanf("%lf",&map[i][j]); memset(vis,false,sizeof(vis)); memset(ans,0,sizeof(ans));Min=INF; dfs(1,1); for (i=1;i<=m;i++) if (i==m) cout<<ans[i]<<endl; else cout<<ans[i]<<" "; } return 0; }