HDU 4253 Two Famous Companies

题目大意:给出三个数n,m,k,代表有n个点m条边,边分为两种一种A类一种B类,建一棵最小生成树其中A类的边要刚好用到k条,问最小花费是多少。


思路:最小生成树+二分,要使得A类边正好用到k条,可以假设如果刚开始是一条也取不到,那么原因是A类的边权值很大,随着权值的减少建最小生成树时取到A类的边会越来越多,那么我们可以用二分来逐渐修改A类边的权值,使得最小生成树A类的边逼近k条,这时二分的左端点就需要是负数了。还有对结构体排序时要先权值优先,权值相等的边要优先选取A类的。



#include<cstdio>
#include<algorithm>
#include<cstring>
#define inf 0x3fffffff
using namespace std;
const int maxn=50050;
int pre[maxn];
struct node
{
    int u,v,w,tmp,id;
}edge[maxn*2],edge2[maxn*2];
void creat()
{
    for(int i=0;i<maxn;i++)
        pre[i]=i;
}
int findroot(int root)
{
    if(root==pre[root])
        return root;
    return pre[root]=findroot(pre[root]);
}
bool cmp(node a,node b)
{
    if(a.w==b.w)
        return a.tmp<b.tmp;
    return a.w<b.w;
}
bool cmp1(node a,node b)
{
    return a.id<b.id;
}
int main()
{
    int n,m,k;
    int u,v,w,tmp,cas=1;
    while(scanf("%d %d %d",&n,&m,&k)!=EOF)
    {
        creat();
        for(int i=0;i<m;i++)
        {
            scanf("%d %d %d %d",&edge[i].u,&edge[i].v,&edge[i].w,&edge[i].tmp);
            edge[i].id=edge2[i].id=i;
            edge2[i].u=edge[i].u,edge2[i].v=edge[i].v,edge2[i].w=edge[i].w,edge2[i].tmp=edge[i].tmp;
        }

        int sum=0,num=0,ans,res;
        int l=-50001,r=50001;
        while(l<=r)
        {
            int mid=(l+r)/2;
            for(int i=0;i<m;i++)
            {
                if(edge2[i].tmp==0)
                {
                    edge[i].w=edge2[i].w+mid;
                }
            }
            sort(edge,edge+m,cmp);
            sum=0,num=0;
            creat();
            for(int i=0;i<m;i++)
            {
                int x=findroot(edge[i].u);
                int y=findroot(edge[i].v);
                if(x!=y)
                {
                    pre[x]=y;
                    sum+=edge[i].w;
                    if(edge[i].tmp==0)
                        num++;
                }
            }
            if(num<k)
            {
                r=mid-1;
            }
            else
            {
                res=sum;
                ans=mid;
                l=mid+1;
            }
            sort(edge,edge+m,cmp1);

        }
        printf("Case %d: %d\n",cas++,res-k*ans);   //如果是正的要减回去,负的要加回来
    }
    return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值