zoukankan      html  css  js  c++  java
  • 基于ZJOI 2012 Day2 T1的修改题: 破坏基地(方法:基于最短路做模拟)


       问题描述:      

        在Z国和W国之间一直战火不断。好不容易,W国的间谍把完整的Z国的军事基地的地图到手了。于是W国决定再次出击,一举击

      破国的防线。

        W国认真研究了Z国的地形,发现Z国有N个军事基地,我们不妨编号成1..N,而且经过深刻研究,发现1号军事基地是资源补

      给基地,而N号军事基地是前线。由于地形的缘故,只有M对军事基地两两可达,当然是有距离的。此时W国的弹头紧缺,当下的弹

      头只能去毁灭一个军事基地。当然了,最重要的就是毁灭一个军事基地,使得资源补给基地与前线的最短距离发生变化。但是

      也不是白痴,他们的资源补给基地与前线有着极高的防御力,所以W 国只能去炸掉其余的N-2个基地,当然炸掉某个基地后,这个

      基地就不可达了。于是问题就来了,炸掉哪些基地后会使得资源补给基地与前线的最短距离发生变化呢?注:假若炸掉某个基地后

      ,1号基地和N号基地不连通,那么我们也认为他们的最短距离发生了变化。


       输入格式(destroy.in  ):

        输入数据第一行是两个正整数NM,意义如题所述。
        接下来M行,每行包括三个正整数xyd,表示有一条边连接xy两个地点,其距离为d。数据保证图是连通的。


       输出格式(destroy.out):

        输出数据的第一行包含一个整数K,表示有K个基地可毁灭,且毁灭其中任意一个后,资源补给基地与前线的最短距离发生变化

      。接下来K行,每行输出一个军事基地的编号,要求编号递增。

        在wyl8899神犇的率领下,W国必胜!!! 
        因此一定不会存在K=0的情况。


       输入样例:

                      6 7

                      1 2 3

                      1 5 2

                      2 3 5

                      2 4 3

                      2 5 4

                      2 6 5

                      3 4 2


       输出样例:

                           1

                      2


     解题:

          首先,先了解题目的大意:要求求出所有从点1 到点n 的最短路必定经过的点。

          所以,开始想能否直接gfs ,如果可以,那么答案的求法为:

            设有两条最短路,7个点:

              第1 条最短路:途经的点为1011011(第 i 个点为1代表有经过第 i 点,反之则为 0 );

                第2 条最短路:途经的点为1001001

            若第 i 点在所有路中都为1 ,那么 i 就是一个答案。

          接着,数据中 n 为10000,m为100000,所以肯定不能裸裸的广搜,深搜就更不可以了— —! 毕竟要不断的搜,不断

        的更新最短路径。也就是说不能用搜索去求最短路,但求最短路还是有很多的方法的,狄杰斯科拉等等。本题解采用狄杰斯科

        拉。毕竟很无脑。。很好打。。Orz。。最短路有什么用下文会说。。

          最后,就是分析了,之前之所以不能裸裸的广搜原因是什么?经过分析很容易发现是因为有一堆完全没有用处的边,占据

        了大量的空间和时间。所以就要考虑是否能去掉这些无用的边,答案就是构造最短路可拓扑图,设求出的点1 到第 i 个点的最

        短距离为 d[ i ] ,那么对于给出的所有路中,如果存在给出的边(a,b,c),使得d[ a ] + c = d[ b ] 那么这条边就是有价值

        的!这是个很可喜的发现,因为这意味着:针对该题,完全可以使用搜索,只不过把搜索的范围限定在最短路经过的边!


      算法实现:

          先求出第一个点到其它点的最短路

          从第n 个点(终点)开始倒着搜索:

          对于每个点,它所能到达的那些点中,只拓展满足上诉的有价值边。

          也就是说,利用预处理过的点1 到个点的最短路、来判定从点1 到点n 的最短路中经过的点,具体看代码


       代码:

        

               
     1 #include<cstdio>
     2 int n,m,x,y,d,t=0,mindis[10010],tot=0,num[10010];//mindis存点1到各点的最短距离,num[i]为所有最短路中经过i点的次数
     3 int wei[10010],las[200010],too[200010],dis[200010],check[10010];  //前四个位哈希数组,后一个为dijkstra的判定数组
     4 void link(int x,int y) { las[++t]=wei[x]; wei[x]=t; too[t]=y; dis[t]=d;}//将一条边添加进哈希数组
     5 void sear(int b)    {                     //从b点拓展
     6     for (int i=wei[b]; i; i=las[i])       //链表枚举能到的点
     7     if (mindis[too[i]]+dis[i]==mindis[b]) //判定是否为有价值的边,是才执行
     8     if (too[i]==1) tot++; else {          //如果走到了起点那么方案加一
     9         num [too[i]]++;                      //经过too[i]点的次数加一
    10         sear(too[i])  ;                      //拓展该有价值的点
    11     }
    12 }
    13 void print(int x,int t) {                 //这是输出,从第n个点开始枚举
    14     if (x>1) {
    15         if (num[x]==tot) t++;             //一个点是答案的前提是经过次数等于方案数,如果相等,总点数+1
    16         print(x-1,t);                      //因为x>1,代表没扫完,所以继续扫前一个点
    17         if (num[x]==tot) printf("%d
    ",x);//输出该点
    18     }  else printf("%d
    ",t);              //如果扫到了第一个点,就输出总必经点数
    19 }                                          //采取递归,以便满足输出顺序为:方案数》》从小到大个点
    20 int main(){
    21     scanf  ("%d%d"  ,&n ,&m    );      //读入数据,连接双向边
    22     for (int i=1; i<=m; i++) {
    23         scanf("%d%d%d",&x,&y,&d);
    24         link(x,y);     link(y,x);
    25     } 
    26     for (int i=0     ; i<=n; i++     ) mindis[i] = 899999999; //初始化到所有点的最小距离
    27     for (int i=wei[1]; i   ; i=las[i]) mindis[too[i]]=dis[i]; //开始dijkstra
    28     for (int i=1     ; i< n; i++     )  {                      //
    29         int f=0;                                              //
    30         for (int j=1 ; j<=n; j++     )                          //
    31          if ((!check[j]) && mindis[f]>mindis[j]) f=j;          //
    32         check[f]=1;                                           //这片可以无视,只要求得出点1到各点的最短路
    33         for (int j=wei[f]; j;j=las[j])                        //并存在mindis数组内都ok
    34          if (mindis[too[j]]>mindis[f]+dis[j])                 //
    35              mindis[too[j]]=mindis[f]+dis[j];                 //
    36     }                                                          //结束dijkstra
    37     mindis[1]=0; sear(n); print(n-1,0);                       //深搜,输出结果
    38 }                                                              //啦啦啦38行—— ——!
    38行~最短路加搜索~

       后记:

        lazycal巨神告诉我要用spfa。。测试机好的话狄杰斯科拉没事,但不好的话还是会T。。。所以要用spfa~~Orz

     


                                          END。

  • 相关阅读:
    堆排序
    阿里云
    ubuntu下编译内核模块
    字节对齐
    线段树
    c++虚函数表解析
    电面
    sql server数据库定时自动备份
    [hiho1584]Bounce
    五彩斑斓的世界
  • 原文地址:https://www.cnblogs.com/qq359084415/p/3391262.html
Copyright © 2011-2022 走看看