zoukankan      html  css  js  c++  java
  • VIJOS P1540 月亮之眼

    【题目大意】

             有多个珠子,给出部分珠子之间的相对上下位置和间距,问你这些珠子在满足给出的条件下,是否能把珠子排列在一条竖直直线上,如果能,求出每个珠子距离最高的珠子的距离,珠子的位置可重叠。

    【分析】

            可以根据珠子的位置关系建立一张有向图,A->B 为A比B高,权值为之间的距离。可以发现必须满足下列三种情况:

                   1、图有连通;无法比较出不同连通分支的上下关系。
                   2、有向图没有环;根据位置的传递关系,不可能自己比自己低。
                   3、如果从A到B有多条路径,路径的长度都应该一样;要不然B的位置关系就会有二义性。

           我本来的想法是按顺序验证上面三条规则,把有向图转为无向图判联通,用拓扑排序判环,用DFS来判路径长度,想想太复杂了。后来想想其实没必要那么复杂。对于每条有向边,添加一条负权反向边,任意选定一个点,假设它是最高点,离最高点的距离是0,用DFS搜索可连向的点,如果新点未访问,则新点的距离就是当前点的距离加上边的权值,如果新点被访问过,这需要验证当前点的距离加上边权是否与新点原来的权值相等,如果不相等,则说明有冲突。如果DFS结束还有点没被访问,则说明图不连通。如果发现有点的距离为负数,则说明那个点比搜索开始的点的距离更高。

        DFS的代码很简单:

     1 #include <stdio.h>
     2 #include <string.h>
     3 struct node
     4 {
     5     int x,y,c,next;
     6     void mset(int a,int b,int z,int nn)
     7     {
     8         x=a;
     9         y=b;c=z;
    10         next=nn;
    11     }
    12 }ev[5000];
    13 int ej,n,p;
    14 int map[502];
    15 int vv[502][502];
    16 int num[502];
    17 int dis[502];
    18 bool ans;
    19 int mmin;
    20 int min(int a,int b){return a<b?a:b;}
    21 void insert(int x,int y,int v)
    22 {
    23     int tem=map[x];
    24     ev[ej++].mset(x,y,v,tem);
    25     map[x]=ej-1;
    26     vv[x][y]=v;
    27     vv[y][x]=-v;
    28 }
    29 void dfs(int x)
    30 {
    31     if (num[x]) return;
    32     num[x]=1;
    33     int p=map[x];
    34     while (p!=-1 && ans)
    35     {
    36        node tt=ev[p];
    37        int t2=dis[x]+tt.c;
    38        if (dis[tt.y]!=-1 && dis[tt.y]!=t2)
    39         {
    40             ans=false;
    41             return;
    42         }
    43        dis[tt.y]=t2;
    44        dfs(tt.y);
    45        p=tt.next;
    46     }
    47 }
    48 int main()
    49 {
    50     ej=0;mmin=0;
    51     ans=true;
    52     //freopen("in.txt","r",stdin);
    53     memset(map,-1,sizeof map);
    54     memset(num,0,sizeof num);
    55     memset(dis,-1,sizeof dis);
    56     scanf("%d%d",&n,&p);
    57     for (int i=0;i<p;++i)
    58     {
    59         int a,b,c;
    60         scanf("%d%d%d",&a,&b,&c);
    61         insert(a,b,c);
    62         insert(b,a,-c);
    63         num[b]=1;
    64     }
    65     int p=0;
    66     for (int i=1;i<=n;++i)
    67       if (num[i]==0) {p=i;break;}
    68     memset(num,0,sizeof num);
    69     if (p==0)
    70     {
    71         printf("-1
    ");
    72         return 0;
    73     }
    74     //printf("%d
    ",p);
    75     dis[p]=0;
    76     dfs(p);
    77     if (!ans)
    78     {
    79         printf("-1
    ");
    80     } else
    81     {
    82         for (int i=1;i<=n;++i) mmin=min(mmin,dis[i]);
    83          for (int i=1;i<=n;++i) printf("%d
    ",dis[i]-mmin);
    84     }
    85  
    86 }
    View Code

     另外还可以使用并查集的方法,A是B的双亲就表示A的位置比B的高,对于每个节点保存当前到双亲节点的距离(为正值),这样在并查集的树中,根节点与一个孩子的孩子的孩子.....的孩子的距离就可以在路径压缩的过程中计算出来:

    int find(int x)
    {
        if (mset[x]==-1) return x;
        int t=mset[x];
        d[x]+=d[t];  //递推出与根节点的距离
        return mset[x]=find(t);
    }

    对于两个不同集合的合并,由于在找集合的过程中使用了find函数,所以相关节点一定直接和集合树的根节点连接。设现在要连接的节点是a、b,它们的根节点分别是roota和rootb, a与b的距离为c, a在b的上面。集合的合并是集合根节点之间的连接,所以需要计算出根节点之间的距离P,b到roota的距离应为d[a]+c, b到rootb的距离为d[b] ,假设rootb成为roota的孩子,着P+d[b]=d[a]+c => P=d[a]+c-d[b].若P为负,则roota应成为roota的孩子,距离为P的绝对值。

     1 #include <stdio.h>
     2 #include <string.h>
     3 int mset[500];
     4 int d[500],n,p;
     5 int find(int x)
     6 {
     7     if (mset[x]==-1) return x;
     8     int t=mset[x];
     9     d[x]+=d[t];
    10     return mset[x]=find(t);
    11 }
    12  
    13 int main()
    14 {
    15     while (~scanf("%d%d",&n,&p))
    16     {
    17         memset(mset,-1,sizeof mset);
    18         memset(d,0,sizeof d);
    19         bool ans=true;
    20         for (int i=0;i<p && ans;++i)
    21         {
    22             int a,b,c;
    23             scanf("%d%d%d",&a,&b,&c);
    24             int xx=find(a);
    25             int yy=find(b);
    26             int p=d[a]+c-d[b];
    27             if (xx!=yy)
    28             {
    29                 if (p>=0)
    30                 {
    31                     mset[yy]=xx;d[yy]=p;
    32                 } else
    33                 {
    34                     mset[xx]=yy;d[xx]=-p;
    35                 }
    36             } else
    37             {
    38                 if (d[a]+c!=d[b]) ans=false;
    39             }
    40         }
    41         for (int i=1;i<=n;++i) find(i);
    42         if (ans)
    43            for (int i=1;i<=n;++i) printf("%d
    ",d[i]);
    44         else printf("-1
    ");
    45     }
    46 }
    View Code

    这里能用并查集是利用了孩子和双亲关系的可传递性。

  • 相关阅读:
    Android : reletive layout
    Android Studio 使用图片
    Android 笔记一:线性布局
    Qt笔记——右键菜单
    Qt笔记——连接第三方库&用libZPlay库获取音频文件的艺术家、专辑等信息
    git常用命令总结
    Qt项目——数字内容管理系统的参考资料和细节
    .Net转Java自学之路—基础巩固篇二十二(XML)
    .Net转Java自学之路—基础巩固篇二十一(模块化思想)
    .Net转Java自学之路—基础巩固篇二十(Lambda)
  • 原文地址:https://www.cnblogs.com/wuminye/p/3182467.html
Copyright © 2011-2022 走看看