51nod1640-最小生成树&二分|性质-天气晴朗的魔法

https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1640
首先要求生成树的最大边最小,然后再要求生成树权值和最大。。
开始写了一个二分。。明显是没有理解krusal。 开始写的是这样。。
这里写图片描述。。
每次生成的mst都是一样的。。(我边是从大到小的。。)如果只有最大的边可以二分成功,其他的都失败。。
想用上一次二分最短路最大值那种方法的。。尴尬。。(用最大值卡边的大小)。我这样离线维护max,mst是不会变的。不能做到每次二分都根据最大值来生成不同的mst。正确的做法是

#include <bits/stdc++.h>
using namespace std;
/*
二分+mst把。
*/
typedef long long ll;
const int MAXN=1e5+300;//最大点数
const int MAXM=250005;//最大边数
int F[MAXN];//并查集使用

struct Edge
{
    int u,v;
    ll w;
}edge[MAXM];//储存边的信息,包括起点/终点/权值

int tol;//边数,加边前赋值为0

void addedge(int u,int v,ll  w)
{
    edge[tol].u=u;
    edge[tol].v=v;
    edge[tol++].w=w;
}

bool cmp(Edge a,Edge b)//排序函数,边按照权值从小到大排序
{
    return a.w>b.w;
}

int Find(int x)
{
    if(F[x]==-1)
        return x;
    else
        return F[x]=Find(F[x]);
}
typedef long long ll;
ll Kruskal(int n,ll  maxcos)//传入点数,返回最小生成树的权值,如果不连通返回-1
{
    memset(F,-1,sizeof(F));
    sort(edge,edge+tol,cmp);
    int cnt=0;//计算加入的边数
    ll  ans=0;
    ll max2=-1;
    for(int i=0;i<tol;i++)
    {
        int u=edge[i].u;
        int v=edge[i].v;
        ll w=edge[i].w;
        int t1=Find(u);
        int t2=Find(v);
        if(w>maxcos) continue;
        if(t1!=t2)
        {
            ans+=w;
            F[t1]=t2;
            cnt++;
        }
        max2=max(max2,1ll*w);
        if(cnt==n-1)
            break;
    }
    //if(maxcos<0)return -1;
    //if(max2>maxcos) return -1;
    if(cnt<n-1)
        return -1;//不连通

    else
        return ans;
}
int main()
{   int m,n,a,b;ll c;
    while(~scanf("%d%d",&m,&n)){
          ll max1=-1;
          ll min1=1e16;
          for(int i=0;i<n;i++){
             scanf("%d%d%lld",&a,&b,&c);
             addedge(a,b,c);
             max1=max(max1,c);
             min1=min(min1,c);
          }
          ll  l=0;
         ll r=max1*2;
         //cout<<Kruskal(m,5)<<endl;
          ll ans=0;
          while(l<r){
              ll mid=(l+r)/2;
              ll dd=Kruskal(m,mid);
              if(dd!=-1){
                 ans=dd;
                 //cout<<mid<<endl;
                 r=mid;
              }
              else
                l=mid+1;
          }
          printf("%lld\n",ans);
    }
    return 0;
}

② 看注释把。那么长

#include <bits/stdc++.h>
using namespace std;
/*我脑子是怎么想的,
这道题的思路 明明是二分维护一个最大值。
然后再这个限制内建造一个mst。如果能建造一个mst的话
那么就建立。
我却写的是 二分维护一个最大值
然后,注意是然后! 建造一个mst.如果这个mst的最大边大于维护的这个值。
就不可以!!!
注意注意! ,竟然没有发现这样建造的mst只有一个,我tm。。

一个图的所有生成树中的最小的最大边,就是他的mst上的
最大边。
所以,朋友们,我们只需要计算即可
*/
int m,n;
const int maxn=2e5+200;
typedef long long ll;
struct Node{
   int from;int to,cost;
}edge[maxn];
int fa[maxn];
int tol;
void add(int a,int b,int c){
     edge[tol].to=b;
     edge[tol].cost=c;
     edge[tol++].from=a;
}
int find1(int a){
    if(fa[a]==a) return a;
     return fa[a]=find1(fa[a]);
}
int unite(int x,int y){
     int a=find1(x);
     int b=find1(y);
     if(a!=b){
        fa[a]=b;
     }
}
void init(){
   tol=0;
   for(int i=0;i<maxn;i++)
      fa[i]=i;
}
bool cmp2(Node a,Node b ){
    return a.cost>b.cost;
}
bool cmp1(Node a,Node b){
    return a.cost<b.cost;
}
int main()
{   int m,n,a,b,c;
    while(~scanf("%d%d",&m,&n)){
          init();
          for(int i=0;i<n;i++){
              scanf("%d%d%d",&a,&b,&c);
              add(a,b,c);
          }
          sort(edge,edge+tol,cmp1);
          int val=-1;
          for(int i=0;i<tol;i++){
              int u=find1(edge[i].from);
              int v=find1(edge[i].to);
              int sum=edge[i].cost;
              if(u!=v){
                unite(u,v);
                val=max(sum,val);
              }
          }
          for(int i=0;i<maxn;i++){
              fa[i]=i;
          }
          ll all=0;
          sort(edge,edge+tol,cmp2);
         for(int i=0;i<tol;i++){
              int u=find1(edge[i].from);
              int v=find1(edge[i].to);
              int sum=edge[i].cost;
              if(sum>val) continue;
              if(u!=v){
                unite(u,v);
                all+=sum;
              }
          }
          printf("%lld\n",all);
    }
    return 0;
}

PS:2018.5.2 大家还要注意kruskal里那个也是如果边数已经等于 n-1就退出也要写,不然在边很多但是早已建成生成树的情况下,可能会TLE(事实上去掉之后在最后一组数据上就TLE了)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值