• LuoguP1606 [USACO07FEB]荷叶塘Lilypad Pond 【最短路】By cellur925


    最短路好题!】

    参考资料:学长 https://blog.csdn.net/TSOI_Vergil/article/details/52975779 学长太强了!!!%%%

    题目传送门

    ==================算法部分=====================

    我们读完题后,就能感受到浓浓的最短路氛围。我们可以将水与莲花间连边,水水间连边,边权为1;莲花间各自连边,边权为0。这样我们跑完一遍spfa后,就能得到摆放莲花的最小数量。但是第二问就很棘手,聪明的你也许会说跑一遍最短路计数就行了。但其实这是错误的算法。

    比如在这张图中。正方形是莲花,圆是水(后连的莲花)

    最短路条数为2,但有3个莲花。

    或者更理性的说,如果经过的水面相同而经过的莲花不同,会被算作是不同的最短路,而实际上方案却是一样的。

    不同的最短路要求很低,只要经过的点有一点不同便不同。而适合本题的求解则是只能水面不同,已有的莲花不同不算不同,也就是说 跑最短路时只经过水面。

    再冷静分析我们会发现已有的莲花间的边是无意义的,只是“为了保证连通性”。同理,除起点和终点外的莲花与水之间的边也是没有意义的。

    所以 我们考虑重新建图,对于每个水,对它进行bfs,把它和它能到达的水连边(或作可达性标记);将起点终点与水连边,这样跑最短路 。我们发现经过这样建图后最短路上的点除起点和终点是莲花之外,其余的点全是水,这样不同的最短路就对应着经过了不同的水,也就对应着不同的放莲花的方案。

    以上有很多都参考Vergil学长的分析==

    ==================代码实现部分========================

    然而算法说起来简单,实现就有些难了。学长给出的代码我太弱不能理解,于是便参考了金涛dalao的写法。从他的代码中对搜索有了更深理解。

    先来观摩一下学长的代码,比较妙的地方是把矩阵的横纵坐标压成序号。

      1 #include<cstdio>
      2 
      3 #include<cstring>
      4 
      5 #include<algorithm>
      6 
      7 using namespace std;
      8 
      9 #define maxn 130005
     10 
     11 int n,m,g[35][35],sx,tx,sy,ty,l,cnt;
     12 
     13 int pre[maxn],last[maxn],other[maxn];
     14 
     15 int quex[1000000],quey[1000000],que[1000000];
     16 
     17 const int dx[9]={0,1,1,2,2,-1,-1,-2,-2};
     18 
     19 const int dy[9]={0,-2,2,-1,1,-2,2,-1,1};
     20 
     21 int flag[35][35],dis[1005];
     22 
     23 bool vis[1005][1005],vist[maxn];
     24 
     25 long long f[1005];
     26 
     27  
     28 
     29 void connect(int x,int y)
     30 
     31 {
     32 
     33     l++;
     34 
     35     pre[l]=last[x];
     36 
     37     last[x]=l;
     38 
     39     other[l]=y;    
     40 
     41 }
     42 
     43  
     44 
     45 void bfs(int fx,int fy)
     46 
     47 {
     48 
     49     flag[fx][fy]=++cnt;
     50 
     51     int h=1,t=0;
     52 
     53     for (int i=1;i<=8;i++) 
     54 
     55     {
     56 
     57         int xx=fx+dx[i];
     58 
     59         int yy=fy+dy[i];
     60 
     61         if (xx<1||xx>n||yy<1||yy>m) continue;
     62 
     63         if (flag[xx][yy]==cnt) continue;
     64 
     65         flag[xx][yy]=cnt;
     66 
     67         if (g[xx][yy]!=2&&g[xx][yy]!=0) 
     68 
     69         {
     70 
     71             t++;
     72 
     73             quex[t]=xx;quey[t]=yy;
     74 
     75             int id1=(fx-1)*m+fy;
     76 
     77             int id2=(xx-1)*m+yy;
     78 
     79             if (vis[id1][id2]) continue;
     80 
     81             connect(id1,id2);
     82 
     83             connect(id2,id1);
     84 
     85             vis[id1][id2]=vis[id2][id1]=1;    
     86 
     87         }
     88 
     89         else if (g[xx][yy]==0)     
     90 
     91         {
     92 
     93             int id1=(fx-1)*m+fy;
     94 
     95             int id2=(xx-1)*m+yy;
     96 
     97             if (vis[id1][id2]) continue;
     98 
     99             connect(id1,id2);
    100 
    101             connect(id2,id1);
    102 
    103             vis[id1][id2]=vis[id2][id1]=1;    
    104 
    105         }
    106 
    107     }
    108 
    109     while (h<=t) 
    110 
    111     {
    112 
    113         int x=quex[h],y=quey[h];h++;
    114 
    115         for (int i=1;i<=8;i++) 
    116 
    117         {
    118 
    119             int xx=x+dx[i];
    120 
    121             int yy=y+dy[i];
    122 
    123             if (xx<1||xx>n||yy<1||yy>m) continue;
    124 
    125             if (flag[xx][yy]==cnt) continue;
    126 
    127             flag[xx][yy]=cnt;
    128 
    129             if (g[xx][yy]==0) 
    130 
    131             {
    132 
    133                 int id1=(fx-1)*m+fy;
    134 
    135                 int id2=(xx-1)*m+yy;
    136 
    137                 if (vis[id1][id2]) continue;
    138 
    139                 connect(id1,id2);
    140 
    141                 connect(id2,id1);
    142 
    143                 vis[id1][id2]=vis[id2][id1]=1;
    144 
    145             }
    146 
    147             else if (g[xx][yy]!=2&&g[xx][yy]!=0) 
    148 
    149             {
    150 
    151                 t++;
    152 
    153                 quex[t]=xx;quey[t]=yy;    
    154 
    155             }
    156 
    157         }
    158 
    159     }
    160 
    161 }
    162 
    163  
    164 
    165 void spfa(void)
    166 
    167 {
    168 
    169     memset(dis,53,sizeof dis);
    170 
    171     dis[(sx-1)*m+sy]=0;
    172 
    173     que[1]=(sx-1)*m+sy;
    174 
    175     int h=1,t=1;
    176 
    177     while (h<=t) 
    178 
    179     {
    180 
    181         int u=que[h];h++;
    182 
    183         vist[u]=0;
    184 
    185         for (int p=last[u];p;p=pre[p]) 
    186 
    187         {
    188 
    189             int v=other[p];
    190 
    191             if (dis[v]>dis[u]+1) 
    192 
    193             {
    194 
    195                 dis[v]=dis[u]+1;    
    196 
    197                 if (!vist[v]) 
    198 
    199                 {
    200 
    201                     que[++t]=v;
    202 
    203                     vist[v]=1;    
    204 
    205                 }
    206 
    207             }
    208 
    209         }
    210 
    211     }
    212 
    213 }
    214 
    215  
    216 
    217 void solve(void)
    218 
    219 {
    220 
    221     memset(vist,0,sizeof vist);
    222 
    223     que[1]=(sx-1)*m+sy;f[que[1]]=1;
    224 
    225     int h=1,t=1;
    226 
    227     while (h<=t) 
    228 
    229     {
    230 
    231         int u=que[h];h++;
    232 
    233         for (int p=last[u];p;p=pre[p]) 
    234 
    235         {
    236 
    237             int v=other[p];
    238 
    239             if (dis[v]==dis[u]+1) 
    240 
    241             {
    242 
    243                 f[v]+=f[u];
    244 
    245                 if (!vist[v]) 
    246 
    247                 {
    248 
    249                     que[++t]=v;
    250 
    251                     vist[v]=1;    
    252 
    253                 }
    254 
    255             }
    256 
    257         }
    258 
    259     }
    260 
    261 }
    262 
    263  
    264 
    265 int main()
    266 
    267 {
    268 
    269     scanf("%d%d",&n,&m);
    270 
    271     for (int i=1;i<=n;i++) 
    272 
    273         for (int j=1;j<=m;j++) 
    274 
    275         {
    276 
    277             scanf("%d",&g[i][j]);
    278 
    279             if (g[i][j]==3) {sx=i;sy=j;}
    280 
    281             if (g[i][j]==4) {tx=i;ty=j;}
    282 
    283         }
    284 
    285     bfs(sx,sy);
    286 
    287     bfs(tx,ty);
    288 
    289     for (int i=1;i<=n;i++) 
    290 
    291         for (int j=1;j<=m;j++) 
    292 
    293             if (g[i][j]==0) bfs(i,j);
    294 
    295     spfa();
    296 
    297     if (dis[(tx-1)*m+ty]>1e7) 
    298 
    299     {
    300 
    301         printf("-1
    ");
    302 
    303         return 0;
    304 
    305     }
    306 
    307     printf("%d
    ",dis[(tx-1)*m+ty]-1);
    308 
    309     solve();
    310 
    311     printf("%lld
    ",f[(tx-1)*m+ty]);
    312 
    313     return 0;    
    314 
    315 }
    Vergil

    金涛的代码就比较亲民了Orz。我仿照他的代码写了一份。注释里有解释。

      1 #include<cstdio>
      2 #include<queue>
      3 #include<cstring>
      4 #include<algorithm>
      5 #include<utility>
      6 
      7 using namespace std;
      8 typedef long long ll;
      9 
     10 int n,m,bx,by,ex,ey;
     11 int mapp[40][40],dis[40][40];
     12 ll f[40][40];//记得开long long!!
     13 bool vis[40][40];
     14 bool able[40][40][40][40];
     15 int dx[20]={0,1,2,2,1,-1,-2,-2,-1};
     16 int dy[20]={0,-2,-1,1,2,2,1,-1,-2};
     17 
     18 void floodfill(int x,int y)
     19 {//洪水填充,也可以理解为bfs
     20     memset(vis,0,sizeof(vis));
     21     queue<pair<int,int> > q;
     22     q.push(make_pair(x,y));
     23     while(!q.empty())
     24     {
     25         int nowx=q.front().first;
     26         int nowy=q.front().second;
     27         q.pop();
     28         for(int i=1;i<=8;i++)
     29         {
     30             int xx=nowx+dx[i];
     31             int yy=nowy+dy[i];
     32             if(xx<1||xx>n||yy<1||yy>m) continue;
     33             if(vis[xx][yy]) continue;//vis数组实际在检查是不是莲花,但我们要找水
     34             if(!mapp[xx][yy]||(xx==ex&&yy==ey))
     35                 able[x][y][xx][yy]=1;//注意是x,y 我们洪水填充的目的是更新传进的x,y
     36 // able相当于连边了 效果一样
     37             else if(mapp[xx][yy]==1)
     38                 q.push(make_pair(xx,yy)),vis[xx][yy]=1; 
     39 //如果是莲花,就是待扩展节点,加入队列
     40         }
     41     }
     42 }
     43 
     44 void spfa_work()
     45 {//边跑最短路边计数
     46     memset(vis,0,sizeof(vis));
     47     queue<pair<int,int> >q;
     48     q.push(make_pair(bx,by));
     49     memset(dis,127,sizeof(dis));
     50     vis[bx][by]=1;dis[bx][by]=0,f[bx][by]=1;
     51     while(!q.empty())
     52     {
     53         int nowx=q.front().first;
     54         int nowy=q.front().second;
     55         q.pop();vis[nowx][nowy]=0;
     56         for(int i=1;i<=n;i++)
     57             for(int j=1;j<=m;j++)
     58                 if(able[nowx][nowy][i][j])
     59                 {
     60                     if(dis[i][j]>dis[nowx][nowy]+1)
     61                     {
     62                         dis[i][j]=dis[nowx][nowy]+1;
     63                         f[i][j]=f[nowx][nowy];
     64                         if(!vis[i][j]&&(i!=ex||j!=ey))
     65                         {//终点就不用再扩展了
     66                             vis[i][j]=1;
     67                             q.push(make_pair(i,j));
     68                         }
     69                     }
     70                     else if(dis[i][j]==dis[nowx][nowy]+1)
     71                         f[i][j]+=f[nowx][nowy];
     72                 }
     73     }
     74 }
     75 
     76 int main()
     77 {
     78     scanf("%d%d",&n,&m);
     79     for(int i=1;i<=n;i++)
     80         for(int j=1;j<=m;j++)
     81         {
     82             scanf("%d",&mapp[i][j]);
     83             if(mapp[i][j]==3) bx=i,by=j;
     84             if(mapp[i][j]==4) ex=i,ey=j; 
     85         }
     86     floodfill(bx,by);
     87 //从起点出发
     88     for(int i=1;i<=n;i++)
     89         for(int j=1;j<=m;j++)
     90             if(!mapp[i][j]) floodfill(i,j);
     91     spfa_work();
     92     if(dis[ex][ey]>1000) 
     93     {//最多也才900个点
     94         printf("-1");
     95         return 0;
     96     } 
     97     else printf("%d
    %lld",dis[ex][ey]-1,f[ex][ey]);
     98 //注意-1 以及lld
     99     return 0;
    100 }
    View Code

    在这道题中学到了肥肠多的新想法,更深入理解了搜索算法。

    本来最简单的dfsbfs我是不会的,但是学习了图论之后,就能感性理解,现在我们回过头去看那些最简单的:

    bfs 我们常常使用队列实现,而我们要注意到,队列中的点都是待扩展节点。每次我们取出队头的顶点,遍历它能到达的全部状态。

  • 相关阅读:
    你是怎么把字符串“2016-11-16” 变为 “16/11/2016” 的?
    js-------》(小效果)实现倒计时及时间对象
    Ruby方法
    JAVASCRIPT事件详解-------原生事件基础....
    html5的小知识点小集合
    原生js实现的效果
    IDEA 实用功能Auto Import:自动优化导包(自动删除、导入包)
    8.SpringBoot 模板引擎 Thymeleaf
    7.SpringBoot 之 Web
    6.日志的使用
  • 原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9556557.html
Copyright © 2020-2023  润新知