2489 Minimal Ratio Tree 最小生成树(选边)

22 篇文章 0 订阅
4 篇文章 0 订阅

题意: 给n个点及各点间的距离,从中选出m个点 ,使得  边权的和 / 点权的和  最小。

思路: 从n个点中选出m个点(枚举每种可能的情况),计算出它的。。。最小值。所以可以用dfs枚举出各种情况,再用prime算出每种情况的。。。最小值。


#include<iostream>
#include<string.h>
#define INF 999999
using namespace std;
int map[20][20],dis[20];
int vis[20],mark[20],nx[20] , flag[20];
int bian,dian;
int n,m;
int prime(int s  , int temp[20][20])
{
    memset(vis,0,sizeof(vis));
    memset(dis,0,sizeof(dis));
    int sum = 0;
    int min, rec;
    for(int i=1;i<=n;i++) dis[i] = temp[s][i];
    vis[s]=1;
    for(int i=1;i<n;i++)
    {
        min = INF;
        for(int j=1;j<=n;j++)
        {
            if(vis[j]==0 && dis[j]< min)
            {
                min = dis[j];
                rec = j;
            }
        }
        vis[rec]=1;
        sum+=min;
        if(i==m-1)  return sum;  //因为每次dis会更新,且只需找m-1条边的最小生成树。 
        for(int j=1;j<=n;j++)
        if(dis[j]> temp[rec][j])  dis[j]=temp[rec][j];
    }
}
void dfs(int g[20] ,  int t , int pre)
{
     if(t==m)  //挑选的点达到m ,可计算其。。。最小值 
     {
          int sum = 0 , s;
          for(int i=1;i<=n;i++)  if(g[i]==1) sum+=nx[i]; //点权和 
          int temp[20][20];         //用temp记录这挑选出的m个点的边的关系 
          for(int i=1;i<=n;i++)
          {
              for(int j=1;j<=n;j++)
              {
                   if(g[i]==0 || g[j]==0) 
                   temp[i][j] =  temp[j][i] = INF;
                   else
                   {
                       s = i;
                       temp[i][j]=map[i][j];
                       temp[j][i]=map[j][i];
                   }
              }
          }
          int cnt =  prime(s ,temp);
          if((cnt*dian - sum*bian)<0) //与之前的最小值比较 
          {
              bian = cnt;
              dian = sum;
              for(int i=1;i<=n;i++) mark[i] = g[i];
          }
     }
     else 
     {
          for(int i=pre ; i<=n;i++)  //枚举,各个点都有可能出现或不出现在m个点中 
          {
               if(g[i]==0)
               {
                   g[i]=1;
                   dfs(g,t+1 , i);
                   g[i]=0;
               }
          }
     }
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF && (m+n))
    {
         memset(map,0,sizeof(map));
         memset(mark,0,sizeof(mark));
         memset(flag,0,sizeof(flag));
         bian = INF;  dian = 1;
         for(int i=1;i<=n;i++) scanf("%d",&nx[i]);
         for(int i=1;i<=n;i++) for(int j=1;j<=n;j++)
         scanf("%d",&map[i][j]);
         dfs(flag, 0 , 1);
         int count=0;
         for(int i=1;i<=n;i++)
         { 
             if(mark[i]==1) 
             {
                 printf("%d",i);
                 count++;
                 if(count!=m) printf(" ");
             }
         }printf("\n");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值