hihoCoder#1608 : Jerry的奶酪(dp)

本文介绍了一个寻找所有奶酪并从迷宫中逃出的问题。利用广度优先搜索算法(BFS),预处理出起点、终点及各奶酪间的最短路径,并通过动态规划求解最优解。

描述
Jerry是一只聪明的老鼠,正身处一个N × M的2D迷宫中。迷宫的每个格子或者是’0’表示Jerry可以走上去,或者是’1’表示格子上有障碍物,不能走上去。

迷宫中有K个格子上放着一块奶酪。Jerry一开始在左上角(0, 0)的位置,它希望收集所有的奶酪,之后再到达右下角(N-1, M-1)的出口。

已知每一秒Jerry可以移动到上下左右四个相邻的没有障碍的格子。你能求出Jerry最快多少秒之后能收集完奶酪并走出迷宫吗?

输入
第一行包含三个整数N, M和K。

之后N行包含一个N × M的01矩阵,表示迷宫。

再之后K行,每行两个整数(x, y)表示奶酪的位置。

对于30%的数据,1 ≤ N, M ≤ 10, 0 ≤ K ≤ 3

对于60%的数据,1 ≤ N, M ≤ 100, 0 ≤ K ≤ 5

对于100%的数据,1 ≤ N, M ≤ 300, 0 ≤ K ≤ 10

输出
走出迷宫最少花费的时间。如果Jerry无法收集到所有的奶酪,或者无法到达出口,输出-1。

样例输入
5 5 3
01000
01010
00010
01010
00000
0 4
2 2
4 0
样例输出
16
不以获取奶酪为目标的移动是无意义的
预处理出起点、终点以及奶酪之间的最短路径
起点的编号为0,终点的编号为k+1
其余在代码中解释

#include <bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
const int N=500;
char arr[N][N];
int dis[N][N];
int d[N][N];
int dp[5050][20];
int n,m,k;
int X[N],Y[N];
struct node
{
    int x,y;
};
int dirt1[4]={-1,0,+1,0};
int dirt2[4]={0,+1,0,-1};
void bfs(int x,int y)
{
    dis[x][y]=0;
    queue<node>q;
    q.push({x,y});
    while(!q.empty())
    {
        node th=q.front();
        q.pop();
        for(int i=0;i<4;i++)
        {
            int xx=th.x+dirt1[i];
            int yy=th.y+dirt2[i];
            if(xx>=0&&xx<n&&yy>=0&&yy<m&&arr[xx][yy]!='1')
            {
                if(dis[xx][yy]!=-1&&dis[xx][yy]<=dis[th.x][th.y]+1)
                    continue;
                dis[xx][yy]=dis[th.x][th.y]+1;
                q.push({xx,yy});
            }
        }
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>n>>m>>k;
    for(int i=0;i<n;i++)
            cin>>arr[i];
    for(int i=1;i<=k;i++)
        cin>>X[i]>>Y[i];
    X[0]=Y[0]=0;X[++k]=n-1,Y[k]=m-1;//对入口,出口及各个奶酪的位置进行编号。
    for(int i =0;i<=k;i++)
    {
        memset(dis,-1,sizeof dis);
        bfs(X[i],Y[i]);//bfs求出这个编号的点到其余编号的距离
        for(int j=0 ;j<=k;j++)//对d进行赋值
            d[i][j]=dis[X[j]][Y[j]];
    }
    memset(dp,-1,sizeof dp);//-1的赋值表示不存在,也使得不满足条件时直接输出-1
    dp[0][0]=0;
    for(int i=0;i<(1<<k+1);i++)//顺序遍历每一个状态
    {
        for(int j=0;j<=k;j++)
        {
            if(dp[i][j]!=-1)//表示这个位置已经有了这么多奶酪
            {
                for(int q=0;q<=k;q++)
                {
                    int ret=i|(1<<q);//位置的转移及奶酪的获取
                    if(d[j][q]!=-1&&(dp[ret][q]==-1||dp[ret][q]>dp[i][j]+d[j][q]))
                        //地图上可以达到并且(这个状态没有出现过或者出现这个状态的路程大于当前路程)
                        dp[ret][q]=dp[i][j]+d[j][q];//状态转移
                }
            }
        }
    }
    cout<<dp[(1<<(k+1))-1][k]<<endl;//在出口处的位置并且拿到了所有的奶酪
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值