zoukankan      html  css  js  c++  java
  • csp20160904解题报告

    dijkstra+贪心

     

    每次加一个到起始点(首都)距离最小的点。

    然后加边,这个最小点必然通过一条边和已加入的某个点相连,在这个最小点与已加入的点相连的边中,选取最短的一条边加入。

     

    证明如下:

    前提:

    G=<V,E,W>。点x到点y的最短距离为(途中可经过其它点)为D(x,y)或D(y,x),若y=1,则简写为D(1)。若点x和点y存在道路(直达),则其中最短的道路长度为W(x,y)或W(y,x)。

     

    做法:

    I.把V分成两个子集S和T。初始时,S={1},T=V-S。

    II.对T中每一元素t计算D(t)(当前点t到点1的路径中,经过的点仅限为S中的点),根据D(t)值找出T中距点1最短的一结点x,写出1到x的最短路径(当前点t到点1的路径中,经过的点仅限为S中的点)的长度D(x)。在点x与S集合中的点相连的边中,选取最短的一条边加入。

    III.置S为S+{x},置T为T-{x},若T为空,则停止,否则再重复2。

     

    证明1:若x是T中具有最小D值的结点,则D(x)是从a到 x的最短距离。

        假设a到x中另有一条含有T中结点的最短通路,不妨设这个通路中第一个属于T-{x}的结点是t1,于是D(x)=D(t1)+D(t1,x)>D(t1)>D(x)。矛盾,所以得证。

     

    D(t)的方法:

    初始时,如果点t到点1存在道路,则D(t)=W(1,t),否则D(t)=无穷(一个足够大的数)。假设对T中的每一个t已计算了D值。设x是T中D值最小的一个结点,记S’=S+{x},T’=T-{x},令D’(t)表示T’中结点t的D值,则

    D’(t)=min[D(t),D(x)+W(x,t)]。

     

    证明2:求D(t)的方法正确

        从点1到点t的最短路径(不包含T’中的其它结点):

        情况1:它不包含T’中的其它结点,则D’(t)=D(t)。

        情况2:它从点1到点x,然后点x到点t(道路),则D’(t)=D(x)+W(x,t)。

        情况3:它从点1到点x,然后从点x到点t(非道路),则从点x到点t的路径中必经过一点,设为点y。可以理解为从点1到点t的最短路径为点1到点y,再点y到点t(不包含T’中的其它结点),而点1到点y的最短路径经过点x,有D(x)<D(y),但y是T中的点,x是T中新加入的点,y在x之前出现,有D(y)<=D(x),矛盾,所以情况3不可能出现。

        所以根据情况1、2,D’(t)=min[D(t),D(x)+W(x,t)],得证。

     

    证明3:在点x与S集合中的点相连的边中,选取最短的一条边加入,所耗费的代价最小。

        如果点1到点x的最短路径为点1到点y(点x为最小点时的T集合(S集合的补集)中的点),点y到点x(道路),则D(x)=D(y)+D(x,y)>D(y)>D(x),矛盾,所以得证。

     

      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <malloc.h>
      4 #include <stdbool.h>
      5 
      6 #define max_ 1000000000
      7 
      8 int main()
      9 {
     10     struct node
     11     {
     12         long point,len;
     13         struct node *next;
     14     };
     15     long n,m,dis[10001],add[10001],mindis,minadd;
     16     long total=0,i,j,a,b,c,d,dist;
     17     bool vis[10001]={false};
     18     struct node *info[10001],*p;
     19 
     20     scanf("%ld%ld",&n,&m);
     21     for (i=1;i<=n;i++)
     22         info[i]=NULL;
     23 
     24     for (i=1;i<=m;i++)
     25     {
     26         scanf("%ld%ld%ld",&a,&b,&c);
     27         p=(struct node *) malloc (sizeof(struct node));
     28         p->point=b;
     29         p->len=c;
     30         if (info[a]!=NULL)
     31         {
     32             p->next=info[a];
     33             info[a]=p;
     34         }
     35         else
     36         {
     37             p->next=NULL;
     38             info[a]=p;
     39         }
     40         p=(struct node *) malloc (sizeof(struct node));
     41         p->point=a;
     42         p->len=c;
     43         p->next=NULL;
     44         if (info[b]!=NULL)
     45         {
     46             p->next=info[b];
     47             info[b]=p;
     48         }
     49         else
     50         {
     51             p->next=NULL;
     52             info[b]=p;
     53         }
     54     }
     55     for (i=1;i<=n;i++)
     56     {
     57         dis[i]=max_;
     58         add[i]=max_;
     59     }
     60     p=info[1];
     61     while (p)
     62     {
     63         dis[p->point]=p->len;
     64         add[p->point]=p->len;
     65         p=p->next;
     66     }
     67     dis[1]=0;
     68     add[1]=0;
     69     vis[1]=true;
     70     for (i=1;i<n;i++)
     71     {
     72         mindis=max_;
     73         minadd=max_;
     74         for (j=1;j<=n;j++)
     75             if (!vis[j])
     76             {
     77                 if (dis[j]<mindis)
     78                 {
     79                     mindis=dis[j];
     80                     minadd=add[j];
     81                     d=j;
     82                 }
     83                 else if (dis[j]==mindis && add[j]<minadd)
     84                 {
     85                     minadd=add[j];
     86                     d=j;
     87                 }
     88             }
     89         total+=minadd;
     90         vis[d]=true;
     91         //m<100000 , (x,y)对有2*m个,只要判断2*m次即可
     92         p=info[d];
     93         while (p)
     94         {
     95             if (!vis[p->point])
     96             {
     97                 dist=mindis+p->len;
     98                 if (dist<dis[p->point])
     99                 {
    100                     dis[p->point]=dist;
    101                     add[p->point]=p->len;
    102                 }
    103                 else if (dist==dis[p->point] && p->len<add[p->point])
    104                     add[p->point]=p->len;
    105             }
    106             p=p->next;
    107         }
    108     }
    109     printf("%ld
    ",total);
    110     return 0;
    111 }
    112 /*
    113 input:
    114 5 7
    115 5 1 2
    116 5 4 10
    117 1 4 7
    118 1 2 3
    119 2 4 6
    120 3 2 4
    121 4 3 5
    122 output:
    123 16
    124 */

     注意不能直接开[10001][100001]的数组,会直接空间溢出,也不能开[10000][10000]的点,n=10000时不对。

    拓展:

    1 ≤ n ≤ 10000,1 ≤ m ≤ 100000

    上述程序方法每次找最小距离的点(n个),共找n次;(x,y)对有2*m个,只要判断2*m次即可,2*m<=n*n,时间复杂度为O(n^2)。实际上n*n=100000000理应通不过,但实际上过了。

    如果用堆排序,判断2*m次,则最多修改2*m次,每次修改时间复杂度为ln(k)(k为树中点的个数,k<=n),最多要操作ln(10000)*2*100000=1842068次,而不修改的堆排序最坏时间为nlnn=1151292,判断2*m次需要操作200000次,其实算的都是大概的数字,最终的操作次数不会大于算的数字*10,所以用这个方法不会超时。

    另外我的一个同学想了一个方法,当求得一个点到首都1距离最短时,就把最短距离要经过的边都设为0,然后继续求其他点到首都的最短距离。

    这方法不对,不能保证每个点到首都都是最短距离,其实他求的是最小生成树。假如有3个点,点1到点2的有一条长度为点5的道路,点2到点3有一条长度为3的道路,点1到点3有一条长度为6的道路,那么我们应选择点1到点3的路径来保证3到首都的距离是最短的,而不是选择点2到点3的道路。

    求最小生成树(所有点都能通过边到达,边的长度之和最小)的时候,不保证两点之间的距离就是最短路径;同样的,求其他点到一个点的最短路径时,不保证这些路径之和就是最小生成树。但是这两者选取的边的数目都是n-1条(n为点的数目),如果最小生成树的边多于n-1条,则必形成环,删去环里的一条边仍能满足所有点都能通过边到达的条件;而求最短路径,用dijstra的方法,求第k个点时需要S集合中的一个点通过一条边与第k个点相连,每多一个点,最短路径就多一条边。

  • 相关阅读:
    微软职位内部推荐-SENIOR SDE
    微软职位内部推荐-Senior Network Engineer
    微软职位内部推荐-Principal Dev Manager
    微软职位内部推荐-SDE II
    微软职位内部推荐-Sr DEV
    【转载】NIO服务端序列图
    【转载】NIO客户端序列图
    同步与异步
    Linux查找命令
    Spring中Bean的实例化
  • 原文地址:https://www.cnblogs.com/cmyg/p/6632644.html
Copyright © 2011-2022 走看看