HDU 4856 Tunnels (BFS + DP)

题目大意:

给出一个网格矩阵,用‘.’和‘#’表示网格的状态,'.'表示空地,可以通过该网格,'#'表示障碍,该网格不可通过。在该网格中存在一些地下道,给出地下道的入口坐标和出口坐标,要求不重复的经过所有的地下道,在走地下道的时候不计入时间,在从一个地下道的出口到另一个地下道的入口计入时间,且没移动到(上下左右)一个网格,记一个时间,初始位置可以在任意一个地下道的入口处。输出最少用时,如果不能走完所有的地下道,输出-1.

分析:

这是一道2014年西安邀请赛的最后一题,我只想到了BFS求每个地下道的出口到其他的地下道入口的最短距离,下面的就不止如何来解,想用深搜所有的行走路线,很明显会超时,在网上看了一下题解,这是一道BFS+DP的题目;

设dp[j][i]表示已经走完了j(包括第i个地下道)表示的地下道,最后走i地下道所用的最小时间。(注意:j是用十进制表示的二进制,对于二进制数的每一位,0表示没有走过,1表示已经走过,例如:j = 6,二进制表示为j = (110)2,表示第0个地下道没有走,第1,2个地下道已经走完,i表示的是地下道的标号)。

状态转移方程:

dp[ j | ( 1<<i ) ] [ i ] = min( dp [ j | ( 1<<i ) ] [ i ] ,  dp [ j ] [ k ] + dis [ k ] [ i ]);

dis[i][j]表示第i个地下道的出口到第j个地下道的入口的最短距离,如果不可达dis[i][j]=INF;

在求解dp[j|(1<<i)][i]的时候,要求dp[j][k]已经求过值,因为没有搞清楚这一点,贡献了一次WA.

代码:

#include <cstdio>
#include <cstring>
#include <queue>
#define INF 1234567
using namespace std;

struct Tunnels{
    int x1, y1;
    int x2, y2;
}t[20];
struct Node{
    int x;
    int y;
    int value;
};
char grids[20][20];
int n, m, dp[1<<20][20];
int dis[20][20], tmp[20][20], vis[500];
int dirx[] = {-1, 0, 1, 0};
int diry[] = {0, 1, 0, -1};

void init() {
    memset(vis, 0, sizeof(vis));
    for(int i=0; i<n; i++) {
        for(int j=0; j<n; j++) {
            tmp[i][j] = INF;
        }
    }
}

int min(int a, int b) {
    return a < b ? a : b;
}

void bfs(int v) {
    Node start;
    start.x = t[v].x2;
    start.y = t[v].y2;
    start.value = 0;
    tmp[start.x][start.y] = start.value;
    vis[start.x*n+start.y] = 1;
    queue<Node> q;
    q.push(start);
    while(q.size() != 0) {
        Node tmpN = q.front();
        q.pop();
        int tmpx, tmpy, tmpValue;
        for(int i=0; i<4; i++) {
            tmpx = tmpN.x + dirx[i];
            tmpy = tmpN.y + diry[i];
            tmpValue = tmpN.value + 1;
            if(tmpx>=0 && tmpx<n && tmpy>=0 && tmpy<n &&
                vis[tmpx*n+tmpy]==0 && grids[tmpx][tmpy]=='.') {
                vis[tmpx*n+tmpy] = 1;
                Node another;
                another.x = tmpx; another.y = tmpy; another.value = tmpValue;
                q.push(another);
                tmp[another.x][another.y] = another.value;
            }
        }
    }
}

void reqDis() {
    for(int i=0; i<m; i++) {
        init();
        bfs(i);
        for(int j=0; j<m; j++) {
            if(i == j) dis[i][j] = INF;
            else
                dis[i][j] = tmp[t[j].x1][t[j].y1];
        }
    }
}

int ans;
void solve() {
    int des = 1<<m;
    for(int i=0; i<m; i++) {
        for(int j=0; j<des; j++) {
            dp[j][i] = INF;
        }
    }
    for(int i=0; i<m; i++) {
        dp[1<<i][i] = 0;    // 1<<i的值表示已经走过了第i个点,所以dp[1<<i][i] = 0;
    }

    for(int j=0; j<des; j++) {  //0~des-1为第一层循环,0~m-1为第二层循环,不可颠倒。
        for(int i=0; i<m; i++) {
            if(j & (1<<i)) continue; //j&(1<<i)表示第i个点已经在j这个状态里了
            for(int k=0; k<m; k++) {
                dp[j | (1<<i)][i] = min(dp[j | (1<<i)][i], dp[j][k] + dis[k][i]);
                //如果两层循环颠倒,那么在求dp[j|(1<<i)][i]的时候,不能保证dp[j][k]是已经
                //求过值的
                //j | (1<<i)表示将第i个地下道加入到状态j中
            }
        }
    }
    ans = INF;
    for(int i=0; i<m; i++) {
        ans = min(ans, dp[des-1][i]);
    }
}

int main() {
    while(~scanf("%d%d", &n, &m)){
        for(int i=0; i<n; i++) {
            scanf("%s", grids[i]);
        }
        for(int i=0; i<m; i++) {
            scanf("%d%d%d%d", &t[i].x1, &t[i].y1, &t[i].x2, &t[i].y2);
            t[i].x1--; t[i].y1--;
            t[i].x2--; t[i].y2--;
        }
        reqDis();
        solve();
        if(ans < INF)
            printf("%d\n", ans);
        else printf("-1\n");
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值