基于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。

原文地址:https://www.cnblogs.com/qq359084415/p/3391262.html