2008 北京区域赛 Minimal Ratio Tree

/*



题目:

    一个有n个节点的完全图,每条边、每个节点都有权值,在该图上求一个有m

    个节点的生成树,使得该树的边权值和比点权和是所有m个节点的生成树中的

    最小值,如果有多解输出字典序最小的



分析:

    递归枚举m个节点,然后根据prim算法求的该比率,记录最小的边权和比点权

    和最小的那棵树即可



*/

#include <cstdio>

#include <cstring>



const int X = 17;

#define INF 1e9



int map[X][X];

int wv[X],n,m;

bool use[X];

int ans[X];

int mnode[X];

double cnt;

int dis[X];



void prim() //prim算法求最小生成树

{

    memset(use,false,sizeof(use));

    memset(dis,0x7f,sizeof(dis));

    int MIN,k;

    int ret = 0;

    dis[1] = 0;

    for(int i=1;i<=m;i++)

    {

        MIN = INF;

        for(int j=1;j<=m;j++)

            if(!use[j]&&dis[j]<MIN)

                MIN = dis[k = j];

        ret += MIN;

        use[k] = true;

        for(int j=1;j<=m;j++)

            if(!use[j]&&dis[j]>map[mnode[k]][mnode[j]])

                dis[j] = map[mnode[k]][mnode[j]];

    }

    int node_w = 0;

    for(int i=1;i<=m;i++)

        node_w += wv[mnode[i]];

    double temp = ret*1.0/node_w;

    if(cnt>temp)

    {

        cnt = temp;

        for(int i=1;i<=m;i++)

            ans[i] = mnode[i];

    }

}



void solve(int have,int cur)    //递归枚举m个节点

{

    if(have==m+1)

    {

        prim();

        return;

    }

    for(int i=cur;i<=n;i++)

    {

        mnode[have] = i;

        solve(have+1,i+1);

    }

}



int main()

{

    freopen("sum.in","r",stdin);

    freopen("sum.out","w",stdout);

    while(scanf("%d%d",&n,&m),n||m)

    {

        cnt = INF;

        for(int i=1;i<=n;i++)

            scanf("%d",&wv[i]);

        for(int i=1;i<=n;i++)

            for(int j=1;j<=n;j++)

                scanf("%d",&map[i][j]);

        solve(1,1);

        printf("%d",ans[1]);

        for(int i=2;i<=m;i++)

            printf(" %d",ans[i]);

        printf("\n");

    }

    return 0;

}

 

你可能感兴趣的:(tree)