zoukankan      html  css  js  c++  java
  • 2019.3.16 最小生成树之城市改造

    题目描述
    作为规划局长的你接到了城市C的改造任务:城市中有n个标志性建筑,要在这n个标志性建筑之间修建双向道路,使得这n个标志性建筑之间相互连通,以最大程度地方便游客来访。由于市政府资金有限,所以希望成本尽可能低。建设费用有两种支付方式:
    1. 施工公司给出m种套餐供选择,每种套餐可以将方案中的所有标志性建筑连通,总花费为Ci;
    2. 单独铺设两个标志物之间道路,费用为两个标志物之间的欧式距离的平方。
    现在要求你选择最佳的建设方案(可以选择任意个套餐以及铺设任意条道路)。
    输入
    输入第一行包括两个整数n和m,分别表示标志物的数量和可选套餐数量。(以下用1~n分别表示n个标志物)
    然后用m行表示m个套餐,每行第一个数字表示套餐可以连接的标志物数量,第二个数字表示该套餐的费用(费用不高于200w万元),后面的数字分别表示该套餐连通的标志物编号。
    最后n行表示n个标志物的坐标,每行用空格隔开的两个数字(0~3000),分别表示横纵坐标。
    输出
    输出一个数字,表示改造城市的最低花费。
    样例输入
    7 3
    2 4 1 2
    3 3 3 6 7
    3 9 2 4 5
    0 2
    4 0
    2 0
    4 2
    1 3
    0 5
    4 4
    样例输出
    17
    提示
    【数据范围】
    对于100%的数据,1<=n<=1000,0<=m<=8。

    省选NOI- QAQ
    其实就是一个最小生成树加枚举
    由于套餐数据较小 选择先枚举套餐再添加边形成最小生成树
    剪枝:
    (1)将套餐是否选择用2进制保存,是则为1,否则为0;
    (2)Kruscal算法中选择先将每个套餐合并为同一祖先,再通过一个点将两个套餐合并。
    上代码
    #include<iostream>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    long long n,m,pre[1005],link[15],tmp,book[15][1005],x[1005],y[1005],cnt=1;
    long long ans=0x3f3f3f3f;
    static const long long numm[9]={1,2,4,8,16,32,64,128,256};
    struct edge
    {
        long long u,v;
        long long w;
        bool operator<(const edge& o) const
        {
            return w<o.w;//重载小于运算符,将边按照边权排序
        }
    }e[1000005];
    int f(long long a)
    {
        if(pre[a]==a)return a;
        return pre[a]=f(pre[a]);
    }//查找祖先
    void u(long long a,long long b)
    {
        long long c=f(a),d=f(b);
        if(pre[c]!=pre[d])pre[c]=pre[d];
    }//合并
    void Kruscal()//最小生成树
    {
        long long a[15];
        for(long long i=0;i<numm[m];i++)
        {
            for(long long j=1;j<=n;j++)pre[j]=j;//初始化祖先
            long long tmp=i,anss=0;
            for(long long j=m;j>=1;j--)a[j]=tmp%2,tmp/=2;//状压
            for(long long j=1;j<=m;j++)
            {
                if(a[j]==1)
                {
                    long long temp=2;
                    while(1)
                    {
                        u(book[j][temp-1],book[j][temp]);//合并套餐内部
                        temp++;
                        if(book[j][temp]==0)break;
                    }
                    anss+=link[j];
                }
            }
            for(long long j=1;j<=cnt;j++)//加边形成最小生成树
            {
                long long uu=e[j].u,vv=e[j].v;
                if(f(uu)!=f(vv))
                {
                    u(uu,vv);
                    anss+=e[j].w;
                }
            }
            ans=min(ans,anss);//选择费用最小值
        }
    }
    int main()
    {
        scanf("%lld%lld",&n,&m);
        for(long long i=1;i<=m;i++)
        {
            long long num;
            scanf("%lld%lld",&num,&link[i]);//套餐内建筑数目与套餐价格
            for(long long j=1;j<=num;j++)scanf("%lld",&book[i][j]);
        }
        for(long long i=1;i<=n;i++)scanf("%lld%lld",&x[i],&y[i]);
        for(long long i=1;i<=n;i++)
        {
            for(long long j=1;j<=n;j++)
            {
                if(i==j)continue;
                e[cnt].u=i,e[cnt].v=j;
                e[cnt].w=(x[j]-x[i])*(x[j]-x[i])+(y[j]-y[i])*(y[j]-y[i]);//初始化边权
                cnt++;
            }
        }
        sort(e+1,e+cnt);
        cnt--;//将cnt置为边数
        Kruscal();
        printf("%lld",ans);
        return 0;
    }
    /*====年轻人,瞎搞是出不了省一的,这就是现实====*/
  • 相关阅读:
    PHP实现无限极分类
    html2canvas生成并下载图片
    一次线上问题引发的过程回顾和思考,以更换两台服务器结束
    Intellij IDEA启动项目报Command line is too long. Shorten command line for XXXApplication or also for
    mq 消费消息 与发送消息传参问题
    idea 创建不了 java 文件
    Java switch 中如何使用枚举?
    Collections排序
    在idea 设置 git 的用户名
    mongodb添加字段和创建自增主键
  • 原文地址:https://www.cnblogs.com/qxds/p/10584144.html
Copyright © 2011-2022 走看看