hdu 2489 - Minimal Ratio Tree(最小生成树)

题意:大意就是有边权,有点权,求选定m个点,生成一颗树,使k=选定边权/点权 最小。
思路:由k=边权/点权,选m个点时,点权已经固定了,所以要使这些点的边权和最小,自然为最小生成树。枚举+kruskal 枚举由于每个点可以选和不选,而且最多也就20个点,所以直接上2进制,1表示选,0表示不选这个点,然后用这些点来建树。
代码

#include <cstdio>
#include <iostream>
#include <algorithm>

using namespace std;
const int maxn = 40,inf = 0x3f3f3f3f;
struct node
{
    int from,to,w;
    node(){}
    node(int a,int b,int c)
    {from = a; to = b; w = c;}
}edge[maxn*maxn];
int value[maxn],data[maxn][maxn];
int n,m,edgenum;
bool cmp(node a,node b)
{   return a.w < b.w;   }

int f[maxn],vis[maxn];

int getf(int i)
{   return i==f[i]?i:f[i]=getf(f[i]);   }

double kruskal(int sta)
{
    for(int i = 0; i < maxn; i++) f[i] = i,vis[i] = 0;
    int vw = 0,th = 1,num = 0,ew = 0;
    while(sta){if(sta&1)vis[th]=1; th++;sta=(sta>>1);}
    for(int i = 0; i < edgenum; i++)
    {
        int u = edge[i].from,v = edge[i].to;
        if(!vis[u]|| !vis[v]) continue;
        int x = getf(u),y = getf(v);
        if(x!=y)
        {
            f[x] = y;
            num++;
            ew += edge[i].w;
            if(num == m-1) break;
        }
    }

    for(int i = 1; i <= n; i++)
        if(vis[i]) vw += value[i];

    if(num != m-1) return inf;
    else return (double)ew/vw;

}
int main()
{
    while(~scanf("%d%d",&n,&m) && n+m)
    {
        for(int i = 1; i <= n; i++)
            scanf("%d",&value[i]);
        edgenum = 0;
        for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
        {
            scanf("%d",&data[i][j]);
            if(i!=j) edge[edgenum++] = node(i,j,data[i][j]);
        }
        sort(edge,edge+edgenum,cmp);
        double ansa = inf;
        int anssta = 0;
        for(int i = 1; i < (1<<n) ; i++)
        {
            int k = i,ti = 0;
            while(k){if(k&1) ti++; k = (k>>1);}
            if(ti!=m)continue;
            else
            {
                double w = kruskal(i);
                if(w < ansa)    anssta = i,ansa = w;
            }
        }

        int out = 1,flag = 0;
        while(anssta)
        {
            if(anssta&1)
            {
                flag?printf(" %d",out):printf("%d",out);
                flag = 1;
            }
            out++;
            anssta = (anssta >> 1);
        }
        printf("\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值