• 【Kruskal+dfs】BZOJ1016- [JSOI2008]最小生成树计数


    【题目大意】

    现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树。

    【思路】

    拖欠了三个月整(?)的题目,搞出来弄掉了……本年度写的时候姿势最丑的程序,完全不知道自己在搞些什么,晕乎乎的,算了。

    首先,MST具有以下性质:

    1.对于同一张无向加权图G,它的最小生成树中长度为L的边长度一定。

    2.MST用Kruskal做,处理完长度<=x,此时图的连通性是确定的。

    我其实没有读懂第二句话是什么意思,总之大概理解一下,然后乱搞!怎么搞呢。

    先按照普通的Kruskal,按照边长排序,然后排序,然后记录下排序为i的长度有几条边numss,下标为nstart到nend。然后弄出一组MST的解。这个不需要单独搞,只需要在记录边的条数的时候一边操作一边进行Kruskal(详细见程序)。

    注意一下做完之后有可能进行合并操作的次数,也就是选的边是小于N-1的,也就是没有生成树,特判一下。

    接着dfs,枚举每条边取numss个。由于题目条件相同长度的边至多10个,只需暴搜索2^10。每次就判断一下当前这条边左右两边是否已经在同一个并查集里面了,如果不在就可以尝试取这条边合并两段,dfs;或者这条边不取,直接dfs下去。

    嗯,然后乘法原理就好了!

    什么乱七八糟的题解……

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<algorithm>
      5 #include<queue>
      6 #define mod 31011
      7 using namespace std;
      8 const int MAXN=1000+50;
      9 struct node
     10 {
     11     int fr,to,len;
     12     bool operator < (const node &x) const
     13     {
     14         return len<x.len;
     15     }
     16 }edge[MAXN];
     17 int num[MAXN],numss=-1,nstart[MAXN],nend[MAXN];//num[i]长度排序为i的边长需要取多少个,nstart/nend表示长度排序为i的边长排序为几到几 
     18 int n,m,pa[MAXN],tot=0,ans,tmpans; 
     19 int find(int x){return (pa[x]==x?x:find(pa[x]));}
     20 
     21 void dfs(int now,int r,int total,int num)
     22 {
     23     if (num>total) return;
     24     if (now>r)
     25     {
     26         if (num==total) tmpans++;
     27         return;    
     28     }
     29     int u=edge[now].fr,v=edge[now].to;
     30     int fa=find(u),fb=find(v);
     31     if (fa!=fb)
     32     {
     33         pa[fa]=fb;
     34         dfs(now+1,r,total,num+1);
     35         pa[fa]=fa;
     36     } 
     37     dfs(now+1,r,total,num);
     38 }
     39 
     40 void init()
     41 {
     42     scanf("%d%d",&n,&m);
     43     for (int i=0;i<m;i++)
     44     {
     45         int a,b,c;
     46         scanf("%d%d%d",&a,&b,&c);
     47         edge[i]=(node){a,b,c};
     48     }
     49     sort(edge,edge+m);
     50 }
     51 
     52 void kruskal()
     53 {
     54     memset(num,0,sizeof(num));
     55     for (int i=1;i<=n;i++) pa[i]=i;
     56     for (int i=0;i<m;i++)
     57     {
     58         if (i==0 || edge[i].len!=edge[i-1].len)
     59         {
     60             if (i!=1) nend[numss]=i-1;
     61             nstart[++numss]=i;
     62         }
     63         int fa=find(edge[i].fr),fb=find(edge[i].to);
     64         if (fa!=fb)
     65         {
     66             pa[fa]=fb;
     67             num[numss]++;
     68             tot++;
     69         } 
     70     }
     71     nend[numss]=m-1;
     72 }
     73 
     74 void solve()
     75 {
     76     if (tot<n-1) puts("0");
     77     else
     78     {
     79         ans=1;
     80         for (int i=1;i<=n;i++) pa[i]=i;
     81         for (int i=0;i<=numss;i++) 
     82         {
     83             tmpans=0;
     84             dfs(nstart[i],nend[i],num[i],0);
     85             ans=(ans*tmpans)%mod;
     86             for (int j=nstart[i];j<=nend[i];j++)
     87             {
     88                 int u=edge[j].fr,v=edge[j].to;
     89                 int fa=find(u),fb=find(v);
     90                 if (fa!=fb) pa[fa]=fb;
     91             } 
     92         }
     93         printf("%d",ans);
     94     }
     95 }
     96 
     97 int main()
     98 {
     99     freopen("bzoj_1016.in","r",stdin);
    100     freopen("bzoj_1016.out","w",stdout);
    101     init();
    102     kruskal();
    103     solve();
    104     return 0;    
    105 } 
  • 相关阅读:
    快手记录的面试题2
    快手Java实习一二面经(记录的面试题1)
    219. 存在重复元素 II(面试题也考过)
    117. 填充每个节点的下一个右侧节点指针 II(没想到,但是其实蛮简单的)
    116. 填充每个节点的下一个右侧节点指针
    最后来几个快手的面试题吧,先记录下来大概看看
    快手Java实习一二面面经(转载)
    双亲委派模型
    聚集索引与非聚集索引总结(转载)
    136. 只出现一次的数字
  • 原文地址:https://www.cnblogs.com/iiyiyi/p/5699517.html
Copyright © 2020-2023  润新知