【算法学习笔记】87. 枚举路径 SJTU OJ 1999 二哥找宝藏

这个题只用BFS来搜索一次会很麻烦, 因为每次经过一个宝藏之后,要把所有的vis重置(因为可以重复经过同一点, 但是这样会有很多不必要的路径)

看题目的暗示 最多只有5个宝藏  我们要把所有的宝藏收集齐全, 如果确定了收集的顺序, 那么也就确定了路径

那么可以知道 A55的排列一共是120种路径 遍历起来毫无压力

我们枚举所有宝藏的全排列, 然后从起点开始走, 记录整个路径的步数, 最后取最小值即可.

这里生产全排列的方法利用了 STL的next_permutation函数 非常爽....(要引入algorithm)

计算路径长度需要计算的只有 

1.起点到各个宝藏的最短距离

2.每两个宝藏之间的最短距离

所以我们用了一个dp来记忆化存储这些路径 避免重复计算

每个最短距离 用bfs来算就好了

//有一个小优化 就是 C62时一旦有任意两个box之间的距离无法达到 则直接返回-1 这样比较快

#include <iostream>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;

int n,m;
int map[100+5][100+5];
bool vis[100+5][100+5];
int dx[] = {-1,1,0,0};
int dy[] = {0,0,-1,1}; 
int dp[6][6]={0};//dp[i][j] 表示 ibox和jbox 的距离 dp[5][i] 表示起点 到i box的距离
struct Point
{
    int x;
    int y;
    int id;
    int step; 
    Point(int i=0,int j =0):x(i),y(j){
        step = 0;
        id = 0; 
    }
};
Point start;//二哥的起点
Point boxes[5];//最多五个宝藏
int path[5];//枚举的线路
int len = 0; //宝藏的个数


void init(){
    cin>>n>>m;
    for (int i = 1; i <= n; ++i){
        for (int j = 1; j <= m; ++j){
            cin>>map[i][j];
            if(map[i][j]==1){
                boxes[len].x = i;
                boxes[len].y = j;
                path[len] = len;
                len++;
            }
            if(map[i][j]==2){
                start.x = i;
                start.y = j;
                start.id = 2;
                start.step = 0; 
            }
        }
    } 
}


//
int bfs(Point s, Point e){

    queue<Point> q;
    memset(vis,false,sizeof(vis));
    s.step = 0;
    q.push(s);
    while(!q.empty()){
        Point cur = q.front();
        q.pop();
        vis[cur.x][cur.y] = true;
        Point next;
        for (int i = 0; i < 4; ++i)
        {
            next.x = cur.x + dx[i];
            next.y = cur.y + dy[i];
            if(next.x>=1 and next.x<=n and next.y>=1 and next.y<=m)
            {
                if(vis[next.x][next.y])
                    continue;
                next.id = map[next.x][next.y];
                vis[next.x][next.y] = true;
                if(next.id != -1){
                    next.step = cur.step + 1; 
                    if(next.x == e.x and next.y == e.y)//到达了终点
                        return next.step;
                    q.push(next);
                }
            }
        }

    }
    return -1; //找不到
}


int build(){
    //枚举取宝藏的顺序  从而形成路径 求出最小的一个即可 方案数最多 A55 = 120种
    //求一下C62的dp
    //生成dp
    for (int i = 0; i < len; ++i)
    {
        dp[5][i] = bfs(start,boxes[i]);
        if(dp[5][i]==-1)
            return -1;
    }

    for (int i = 0; i < len; ++i)
    {
        for (int j = i+1; j < len; ++j)
        {
            dp[i][j] = dp[j][i] = bfs(boxes[i],boxes[j]);
            if(dp[i][j]==-1)
                return -1;
        }
    }
    int ans = 1<<30;
    do{
        int dis = dp[5][path[0]];
        for (int i = 1; i < len; ++i)
        {
            dis += dp[path[i]][path[i-1]];
        }
        ans = min(ans,dis);
    }while(next_permutation(path,path+len));
    return ans;
}
int main(int argc, char const *argv[])
{ 
    init();
    cout<<build()<<endl; 
    return 0;
}

 

posted @ 2015-07-29 00:07  雨尘之林  阅读(704)  评论(0编辑  收藏  举报