HDU 2489 Minimal Ratio Tree(prim+DFS)

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;
}


posted @ 2017-06-27 12:53  zhchoutai  阅读(135)  评论(0编辑  收藏  举报