题解 P1825 【[USACO11OPEN]玉米田迷宫Corn Maze】

玉米田迷宫题解

一.背景

​ x年x月x日,竞赛老师拿此题问我,然后我玄学过了,于是特来写此题。(谁说dijkstra不能过的??)

二.分析

​ 本题,我们先不考虑有传送阵的情况,发现,其实就是一个最短路(bfs)的模板题,随便弄下就能过,不过,这里多了个传送阵,于是我们就要考虑下怎么做题了。。。

Method 1

考虑bfs。

​ 通过观察发现,本题的边的权已经不止是我们平常所做的只有1的边权了,还多了传送阵之间的0边权,所以,本题存在0、1两种边权,怎么搞呢?

有个东西叫做01bfs,专门处理这种情况,我们把队列修改为双端队列,一个点,如果是被1边权扩展出来的,我们把它插入队尾,反之,如果它是由0边权扩展出来的,我们把它插入队首,其余的跟普通bfs一样。

​ 至于为什么是对的,机智的你用bfs是一层一层的扩展的原理来分析便显然易知

​ 那么如何维护题目中的到达传送阵必须传送呢?很简单,我们在队列里面加维护一个bool值flag,flag=0表示我们准备进入传送阵,flag=1表示我们刚从传送阵出来,即可。(记得flag类讨论)

​ 然后。。。就没然后了。。。

Method 2

同样考虑bfs。

​ 现在,令我们恼火的有两个东西:

​ 1.0边权(最短路不需要考虑)

​ 2.到达传送阵必须启动的条件

​ 那么我们怎么做可以忽视这两点从而使得问题转化为一个普通的bfs/最短路呢?

​ 我们这样考虑:既然,我们到达传送阵后必须启用,那么,为什么我们不直接跳过传送阵呢?于是,对于一个传送阵,我们把它和它所对应的传送阵的相邻格子(即一步可以到达的格子)直接连边,这是,我们发现,我们所有的边权都是1了,一遍bfs/最短路即可!

​ 至于为什么是对的呢?我们发现,我们唯一没有求出起点到最短路的距离的点就是传送阵了,而传送阵一定不是终点(显然),所以,我们可以求得答案!

Method 3

考虑最短路。

​ 我们考虑网络流的"拆点"思想,我们将每个点分为0、1两个状态。

0表示流入,1表示流出,同一个点0号到1号之间的边权为0,不与其他点连边,1号则到相邻的点的0号状态。

而对于传送阵,0号只与对应的传送阵的1号状态连边(边权0),1号状态则与四周点连边(边权1),即可,然后,跑最短路(spfa,dijkstra)即可。

​ 这样便能符合条件,然后乱跑就过了

考虑下优化:其他点我们完全没有必要拆成两个点,我们其他点只管0号状态,而对于传送阵,我们再单独维护0,1两个状态,这时,像method1一样,加维护一个bool表示状态再分类处理即可!

​ 稍微提下:dijkstra只是不能处理负环,正权环还是可以处理的 = =

三.代码

​ 由于Method 1,2太"妙"了(其实是懒得打了),我就不放代码了,放个Method 3的代码:

//#pragma GCC optimize()//手动Ox优化
#include<bits/stdc++.h>
using namespace std;
const int N=601,M=180000;
char a[N][N];
int dx[4]={0,0,-1,1},dy[4]={-1,1,0,0};
struct node{
    int v,w,nex;
}t[M<<1];
struct strom{//传送阵 
    int x[3],y[3],e;
}p[26];
bool ys[M];
int las[M],len;
int dis[M][2];
bool vis[M][2];
priority_queue<pair<int,pair<int,int> > >s;
inline void dijkstra(int X){//dijkstra 
    memset(dis,0x3f3f,sizeof(dis));
    dis[X][0]=0;
    s.push(make_pair(0,make_pair(X,0)));
    while(!s.empty()){
        int x=s.top().second.first;
        int y=s.top().second.second;
        s.pop();
        if(vis[x][y]){
            continue;
        }
        vis[x][y]=1;
        if(!y){//分类讨论 
            for(int i=las[x];i;i=t[i].nex){
                int v=t[i].v,w=t[i].w,T=ys[v];
                if(dis[v][T]>dis[x][y]+w){
                    dis[v][T]=dis[x][y]+w;
                    s.push(make_pair(-dis[v][T],make_pair(v,T)));
                }
            }
        }else{
            for(int i=las[x];i;i=t[i].nex){
                if(t[i].w){
                    continue;
                }
                int v=t[i].v;
                if(dis[v][0]>dis[x][1]){
                    dis[v][0]=dis[x][1];
                    s.push(make_pair(-dis[v][0],make_pair(v,0)));
                }
            }
        }
    }
}
inline void add(int u,int v,int w=0){
    len++;
    t[len].v=v,t[len].w=w;
    t[len].nex=las[u],las[u]=len;
}
int main(){
//  freopen("233.in","r",stdin);
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i){
        scanf("%s",a[i]);
    }
    int S,E;
    for(int i=1;i<=n;++i){
        for(int j=1;j<=m;++j){
            if(a[i][j-1]=='#'){
                continue;
            }
            if(a[i][j-1]=='@'){
                S=(i-1)*m+j;
            }
            if(a[i][j-1]=='='){
                E=(i-1)*m+j;
            }
            if(a[i][j-1]>='A'&&a[i][j-1]<='Z'){//建立传送阵 
                ys[(i-1)*m+j]=1;
                int v=a[i][j-1]-'A';
                int now=++p[v].e;
                p[v].x[now]=i,p[v].y[now]=j;
            }
            for(int k=0;k<4;++k){
                int xx=i+dx[k],yy=j+dy[k];
                if(xx>0&&xx<=n&&yy>0&&yy<=m&&a[xx][yy-1]!='#'){//四周连边 
                    add((i-1)*m+j,(xx-1)*m+yy,1);
                }
            }
        }
    }
    for(int i=0;i<26;++i){
        if(p[i].e){
            add((p[i].x[1]-1)*m+p[i].y[1],(p[i].x[2]-1)*m+p[i].y[2]);
            add((p[i].x[2]-1)*m+p[i].y[2],(p[i].x[1]-1)*m+p[i].y[1]);
        }//传送阵连边 
    }
    dijkstra(S);
    printf("%d",dis[E][0]);
    return 0;
}

转载于:https://www.cnblogs.com/ThinkofBlank/p/10403608.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
USACO2022金组是国际在线判题系统USACO的最高级别,题目难度较高,在该比赛中取得好成绩是一项巨大的成就。以下是对该比赛的一些题目解析。 第一题:“交通计划” 题目要求:给定一个n个节点的有向图,每条边有一个长度,希望添加最少的边使得所有节点连通,求最小生成树的权值和。 解析:该题可以使用Kruskal算法求解,将每条边按权值从小到大排序,再依次加入,判断加入的边是否会形成环,若形成则不加入,直到所有节点连通为止。此时Kruskal算法得到的最小生成树的权值和即为所求。 第二题:“点火计划” 题目要求:给定一个n个节点的有向图,每条边有一个权值和一个点火时长,每个节点有一个点火启动时刻和时刻结束时刻,希望从其中选出一些边点火,使得所有节点都可从点火的边出发到达,且所选点火边的总点火时长最小。 解析:该题可以使用最小费用最大流算法求解。将每条边看做一个容量为1,费用为点火时长的边,源点向节点的点火边容量为1,费用为0的边,节点的点火边向汇点的容量为1,费用为0的边,对这个网络进行最小费用最大流即可得到所选边的总点火时长最小。 第三题:“美味佳肴” 题目要求:给定n个菜品,每个菜品有它的权值和两个类别,希望选出k个菜品,使得选出的菜品数量在每个类别中都不超过$\frac{k}{3}$个,且所选菜品的权值和最大。 解析:该题可以使用动态规划求解。设$f[i][j][k]$表示前i个菜品中,选择j个一类菜品,选择k个二类菜品的最大权值和,状态转移方程为$f[i][j][k]=max(f[i-1][j][k],f[i-1][j-1][k]+a[i],f[i-1][j][k-1]+b[i])$,其中a[i]为i号菜品的权值,若为一类则为该权值,否则为0,b[i]为i号菜品的权值,若为二类则为该权值,否则为0。最终答案为$f[n][$k/3$][$k/3$]。 以上是对USACO2022金组的部分题目的解析,USACO比赛是全球范围内的计算机竞赛,竞争非常激烈,能够在该比赛中脱颖而出是一项非常棒的成就。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值