zoukankan      html  css  js  c++  java
  • [Kruskal][jzyzoj1224]:连接格点

      犯了个很智障的错误,本来以为是读入出了问题,结果往kruskal函数里一看。。。。1-2=-1,-1*1000+1=-999,所以RE了。。。下面给出题面:

      

      由题意我们好像看不出来这是个最小生成树。最初我以为这是一道纯贪心就能AC的题,然而仔细画了画图发现并不是这样,如果用贪心的方法算每列上存在的边的条数并不行,因为一列上可以不存在任意一条边,但是如果说通过一个点生成的长边通向这一列上的另外一点的话,这两点之间是不需要再建立一条边的

      这样的话我们可以用图的思想:已经连通的几条边我们可以看做是权值为0的边,还未连通的边我们可以看做是横向权值为1,纵向权值为2的边,然后建图就可以。不过建图的时候需要注意要把二维变为一维

      但是如果将所有的可建立的边记录起来的话不免有些麻烦,我们可以想一个更简便的方法。

      还是贪心的思想:我们先读入已经连好的边,因为这些连好的边的权值为0,所以我们优先用并查集将这两个点合并,即这两点之间不需要再连额外的边,然后在逐个合并的时候不需要再建立边,因为每条边都是横向或是纵向的,而连接纵向的边的权值相对较小,所以我们可以优先连接纵向的边,然后连接横向的边,这样就能解决这个问题了。

      注意记录已经连接的边数,如果边数等于点数-1的话就不需要再连接了,此时一定为最优解。

      代码如下:

     1 #include<iostream>
     2 #include<cstdio>
     3 using namespace std;
     4 int m,n,father[1000010],ans=0,len=0,sum=0;
     5 int getfather(int x)
     6 {
     7     int y=x;
     8     while (father[y]!=y)    y=father[y];
     9     while (father[x]!=x)
    10     {
    11         int temp=father[x];
    12         father[x]=y;
    13         x=temp;
    14     }
    15     return y;
    16 }
    17 
    18 void init()
    19 {
    20     scanf("%d%d",&m,&n);
    21     for (int i=1;i<=m*n;i++)    father[i]=i;
    22     int x1,y1,x2,y2,i=0;
    23     while    (scanf("%d%d%d%d",&x1,&y1,&x2,&y2)!=EOF)
    24     {
    25         i++;
    26         int f1=getfather((x1-1)*n+y1),f2=getfather((x2-1)*n+y2);
    27         if (f1!=f2)
    28         {
    29             father[f1]=f2;
    30             sum++;
    31         }
    32     }
    33 }
    34 
    35 void kruskal()
    36 {
    37     for (int i=1;i<=n;i++)
    38         for (int j=2;j<=m;j++)//枚举纵向的边,n要放在外层,注意j应当从2开始枚举,防止数组越界
    39         {
    40             if (sum==m*n-1)    return;        //判断是否已建立足够的边
    41             int f1=getfather((j-1)*n+i),f2=getfather((j-2)*n+i);    //注意二维变一维
    42             if (f1!=f2)
    43             {
    44                 father[f1]=f2;
    45                 ans++;    sum++;
    46             }
    47         }
    48     for (int i=1;i<=m;i++)
    49         for (int j=2;j<=n;j++)//枚举横向的边,n放在内层,j从2开始,防止枚举上一行最后一列到本行第一列的边
    50         {
    51             if (sum==m*n-1)    return;        //判断是否已建立足够的边
    52             int f1=getfather((i-1)*n+j),f2=getfather((i-1)*n+j-1);
    53             if (f1!=f2){
    54                 father[f1]=f2;
    55                 ans+=2; sum++;
    56             }
    57         }
    58 }
    59 
    60 int main()
    61 {
    62     init();
    63     kruskal();
    64     printf("%d
    ",ans);
    65     return 0;
    66 }
    AC代码

  • 相关阅读:
    JAVA课后作业
    类中创建对象的个数
    JAVA动手动脑及课后思考
    JAVA课后作业
    java课后思考
    加法
    大道至简第一章伪代码
    解决得到网络流不支持查找的方案
    row_number() over(partition by 列名1 order by 列名2 desc)的使用
    SQL中的特殊符号
  • 原文地址:https://www.cnblogs.com/hinanawitenshi/p/6579720.html
Copyright © 2011-2022 走看看