Picnic Planning

Picnic Planning

20181013
生成树 状压 K限制生成树

题意

矮人虽小却喜欢乘坐巨大的轿车,车大到能够装下不管多少矮人。某天,N(N≤20)个矮人打算到野外聚餐。为了集中到聚餐地点,矮人A 要么开车到矮人B 家中,留下自己的轿车在矮人B 家,然后乘坐B 的轿车同行;要么直接开车到聚餐地点,并将车停放在聚餐地。尽管矮人的家非常大,能够停放无数量轿车,可是聚餐地点却最多仅仅能停放K 辆轿车。给你一张加权无向图,描写叙述了N 个矮人的家和聚餐地点,求出全部矮人开车最短总路程

题解

题本身是一道\(k\)限制生成树裸题,但那东西太迷幻,不会.因为数据规模比较小,暴力能过,所以也没学.这里用的是状压\(+Prim\).
因为根节点只能连不大于\(k\)条边,所以我们暴力枚举根节点能连的边的状态,再将剩下的点跑最小生成树,更新最小代价.

  • \(map\)映射每个人的名字
  • 可能有重边,需要更新两点之间的最短距离
  • 这里我们用邻接矩阵存边,用\(Prim\)跑最小生成树,不然时间复杂度过不去
  • 每次生成树必须加够\(N-1\)条边后才能更新\(ans\)
  • 输出应遵循题目规则
#include<cstdio>
#include<map>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int maxn=430;
int n,k,num,cnt;
int fa[22],ans,bns;
map< string , int >mm;
int ss[22][22];
struct fy
{
    int from,to,d;
    bool operator<(const fy&a)const{return d<a.d;};
}q[maxn];
char str[30],str1[30];
void add(int a,int b,int c){q[++num]=(fy){a,b,c};}
int can(int a)
{
    bns=0;
    int he=0,w=1;
    while(a)
    {
        ++w;
        if(a&1)
        {
            he++;fa[w]=1;
            if(ss[1][w]==ss[0][0])return 0;
            bns+=ss[1][w];
        }
        a>>=1;
    }
    if(he<=k)return he;
    return 0;
}
int find(int a)
{
    while(a!=fa[a])a=fa[a]=fa[fa[a]];
    return a;
}
void ku(int x)//披着羊皮的狼(滑稽)
{
    if(!x)return;
    for(int i=1;i<=num;i++)
    {
        if(q[i].from==1||q[i].to==1)continue;
        int a=find(q[i].from);
        int b=find(q[i].to);
        if(a!=b)
        {
            if(x==(cnt-1))break;
            fa[a]=b;
            x++;
            bns+=q[i].d;
            if(bns>ans)return;
        }
    }
    if(x==(cnt-1))ans=min(ans,bns);
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        mm.clear();cnt=0;num=0;
        scanf("%d",&n);memset(ss,0x3f,sizeof ss);
        mm["Park"]=++cnt;ans=ss[0][0];
        for(int i=1;i<=n;i++)
        {
            int a,b,c;
            scanf("%s%s%d",str,str1,&c);
            if(!mm[str])mm[str]=++cnt;
            if(!mm[str1])mm[str1]=++cnt;
            a=mm[str];b=mm[str1];
            ss[a][b]=min(ss[a][b],c);
            ss[b][a]=ss[a][b];
        }
        scanf("%d",&k);
        for(int i=1;i<=cnt;i++)for(int j=i+1;j<=cnt;j++)
        if(ss[i][j]&&(ss[i][j]!=ss[0][0]))add(i,j,ss[i][j]);
        sort(q+1,q+1+num);
        for(int s=0;s<(1<<(cnt-1));s++)//枚举根节点的状态
        {
            for(int i=1;i<=cnt;i++)fa[i]=i;
            ku(can(s));
        }
        printf("Total miles driven: %d\n",ans);//输出
    }
    return 0;
}

转载于:https://www.cnblogs.com/ffdy/p/9798597.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值