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

 

posted @ 2012-06-13 14:15  yejinru  阅读(176)  评论(0编辑  收藏  举报