Minimal Ratio Tree
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3345 Accepted Submission(s): 1019
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个点的权值和这n个点之间的边的权值。从这n个点之中选出m个点使得这m个点的权值最大,这m个点的生成树中边的权值最小。即Ratio最小。依照升序输出这m个点。
解题思路:
首先n的范围是[2,15],所以能够用dfs搜索使得Ratio最小的点。那么思路基本清晰:首先dfs,搜索全部的点选与不选所得到的最大的Ratio。假设当前状态下得到的Ratio比之前得到的Ratio要小,那么把当前状态的vis数组更新的答案ans数组中。
最后从1到n扫描ans数组就可以保证答案是升序。
參考代码:
#include<stack> #include<queue> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #pragma commment(linker,"/STACK: 102400000 102400000") using namespace std; const double eps=1e-6; const int INF=0x3f3f3f3f; const int MAXN=20; int n,m,mincost[MAXN],node[MAXN],edge[MAXN][MAXN]; bool vis[MAXN],used[MAXN],ans[MAXN]; double temp; double prim() { memset(mincost,0,sizeof(mincost)); memset(used,false,sizeof(used)); int s; for(int i=1; i<=n; i++) if(vis[i]) { s=i; break; } for(int i=1; i<=n; i++) { mincost[i]=edge[s][i]; used[i]=false; } used[s]=true; int res=0; int nodevalue=node[s]; for(int j=1; j<n; j++) { int v=-1; for(int i=1; i<=n; i++) if(vis[i]&&!used[i]&&(v==-1||mincost[v]>mincost[i]))//vis[i]表示第i个点有没有被选中 v=i; res+=mincost[v]; nodevalue+=node[v]; used[v]=true; for(int i=1; i<=n; i++) if(vis[i]&&!used[i]&&mincost[i]>edge[v][i]) mincost[i]=edge[v][i]; } return (res+0.0)/nodevalue; } void dfs(int pos,int num) { if(num>m) return ; if(pos==n+1) { if(num!=m) return ; double tans=prim(); if(tans<temp) { temp=tans; memcpy(ans,vis,sizeof(ans)); } return ; } vis[pos]=true;//选择当前点 dfs(pos+1,num+1); vis[pos]=false;//不选择当前点,消除标记 dfs(pos+1,num); } int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); #endif // ONLINE_JUDGE while(scanf("%d%d",&n,&m)) { if(n==0&&m==0) break; for(int i=1; i<=n; i++) scanf("%d",&node[i]); for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) scanf("%d",&edge[i][j]); temp=INF; dfs(1,0); bool flag=false; for(int i=1; i<=n; i++) { if(ans[i]) { if(flag) printf(" "); else flag=true; printf("%d",i); } } printf("\n"); } return 0; }