Leetcode LCP 13. 寻宝(bfs+状压dp)

首先发现有用的点只有S,T,M,O,把他们提出来重新编号
通过bfs寻找两两之间的最短路dis。0 <= M的数量 <= 16,0 <= O的数量 <= 40
所以搜索量并不大。
问题是从S开始经过所有M后到达T,如果不考虑O,这就是经典的旅行商问题。
对于每个M点,达到之前必须经过一个O点,M,O并不多,这也就是说Mi经过O到Mj的最短路可以通过枚举O求得,记adis[Mi][Mj] = min(dis[Mi][Ok]+dis[Ok][Mj]),另外S到M同样处理,M到T不需要过O,adis[Mi][T]=dis[Mi][T] ;
我们发现对于adis,这就是旅行商问题,用状压dp就可以求解

int xx[4] = {0,0,1,-1};
int yy[4] = {1,-1,0,0};    
int dp[1<<17][17];
 
class Solution {
public:
    int id[200][200];
    int dis[110][110];
    int odis[110][110];
    int inq[200][200];
    
    void bfs(int x,int y,vector<string>& mase) {
        int idd = id[x][y];
        //dis[idd][idd] = 0;
        int d[200][200];
        memset(d,127/3,sizeof d);
        queue<int> qx;
        queue<int> qy;
        qx.push(x);
        qy.push(y);
        d[x][y] = 0;
        inq[x][y] = 1;
        while(qx.size()) {
            int tx = qx.front(),ty = qy.front();
            qx.pop();
            qy.pop();
            inq[tx][ty] = 0;
            for (int k = 0; k < 4; k++) {
                int nx = tx+xx[k];
                int ny = ty+yy[k];
                if (nx<0 || ny<0 || nx>=mase.size() || ny>=mase[0].size()) continue;
                if (mase[nx][ny] == '#') continue;
                if (d[nx][ny] > d[tx][ty]+1) {
                    d[nx][ny] = d[tx][ty]+1;
                    if (!inq[nx][ny]) {
                        inq[nx][ny] = 1;
                        qx.push(nx);
                        qy.push(ny);
                    }
                }
            }
        }
        for (int i = 0; i < mase.size(); i++)
            for (int j = 0; j < mase[i].size(); j++) 
             if (id[i][j] && id[i][j]!=idd) {
                dis[idd][id[i][j]] = d[i][j];
//cout<<"dis::"<<idd<<" "<<id[i][j]<<" "<<d[i][j]<<"\n";
            }
    }
    
   
 /*   int find(int s,int d) {
        if (dp[s] <= d) return;
        dp[s] = d;
        for (int i = 3; i <= 2+Mtp; i++)
            if (s&(1<<i-1)==0)
                dfs(s|(1<<i-1) , dp[s]+odis[]);
    }*/
    
    int minimalSteps(vector<string>& mase) {
        int Mtp = 0,Otp = 0;
       // int Sx,Sy,Tx,Ty;
        for (int i = 0; i < mase.size(); i++)
            for (int j = 0; j < mase[i].size(); j++) {
                if (mase[i][j] == 'M') {
                    Mtp++;
                } else if (mase[i][j] == 'O') {
                    Otp++;
                }
            }
         int m = 0,o = 0;
        // s -> 1
        // T -> 2
//cout<<Mtp<<" "<<Otp<<"\n";
         for (int i = 0; i < mase.size(); i++)
            for (int j = 0; j < mase[i].size(); j++) {
                if (mase[i][j] == 'M') {
                    m++;
                    id[i][j] = 2+m;
                } else if (mase[i][j] == 'O') {
                    o++;
                    id[i][j] = 2+Mtp+o;
                } else if (mase[i][j] == 'S') {
                    id[i][j] = 1;
                } else if (mase[i][j] == 'T') {
                    id[i][j] = 2;
                } 
            }
// for (int i = 0; i < mase.size(); i++) {
 //   for (int j = 0; j < mase[i].size(); j++)   
  //   if(id[i][j])cout << i << " "<< j << " " << id[i][j] << "\n";
// }
//puts("");
         memset(dis,127/3,sizeof dis);
         memset(odis,127/3,sizeof odis);
         for (int i = 0; i < mase.size(); i++)
            for (int j = 0; j < mase[i].size(); j++) {
                if (id[i][j]) 
                   bfs(i,j,mase);
  //                 cout<<i<<" "<<j<<"\n";
			}
//return -1;
        //MOM
         for (int j = 3+Mtp; j <= 2+Mtp+Otp; j++)
                 for (int k = 3; k <= 2+Mtp; k++) 
                     odis[1][k] = min(odis[1][k],dis[1][j]+dis[j][k]); 
                     
         for (int i = 3; i <= 2+Mtp; i++)
             for (int j = 3+Mtp; j <= 2+Mtp+Otp; j++)
                 for (int k = 3; k <= 2+Mtp; k++)
                  if (i != k){
                     odis[i][k] = min(odis[i][k],dis[i][j]+dis[j][k]); 
                  }
        if (Mtp==0) {
            return dis[1][2]==dis[0][0]?-1:dis[1][2];
        }
        
        for (int i = 3; i <= 2+Mtp; i++)   
            odis[i][2] = dis[i][2];
            
        memset(dp,127/3,sizeof dp);
        //dp[1] = 0;
        //s 0
        //m 1 
        //dp[1][0] = 0;
        
        for (int l = 1; l <= Mtp; l++)
          dp[1<<(l-1)][l] = odis[1][2+l];
          
        for (int s = 1; s < (1<<Mtp); s++)
         // if (s != (s&-s))
            for (int l = 1; l <= Mtp; l++)
               if (s&(1<<l-1))
                for (int k = 1; k <= Mtp; k++) 
                   if (l!=k && (s&(1<<k-1))) {
                    dp[s][l] = min(dp[s][l],dp[s^(1<<l-1)][k]+odis[k+2][l+2]);
               
				} 
        int All = (1<<Mtp)-1,ans = dp[0][0];
        for (int i = 1; i <= Mtp; i++) 
            ans = min(ans,dp[All][i]+odis[2+i][2]);
        return ans==dp[0][0]?-1:ans;
    }
};

我们得到了一副藏宝图,藏宝图显示,在一个迷宫中存在着未被世人发现的宝藏。

迷宫是一个二维矩阵,用一个字符串数组表示。它标识了唯一的入口(用 ‘S’ 表示),和唯一的宝藏地点(用 ‘T’ 表示)。但是,宝藏被一些隐蔽的机关保护了起来。在地图上有若干个机关点(用 ‘M’ 表示),只有所有机关均被触发,才可以拿到宝藏。

要保持机关的触发,需要把一个重石放在上面。迷宫中有若干个石堆(用 ‘O’ 表示),每个石堆都有无限个足够触发机关的重石。但是由于石头太重,我们一次只能搬一个石头到指定地点。

迷宫中同样有一些墙壁(用 ‘#’ 表示),我们不能走入墙壁。剩余的都是可随意通行的点(用 ‘.’ 表示)。石堆、机关、起点和终点(无论是否能拿到宝藏)也是可以通行的。

我们每步可以选择向上/向下/向左/向右移动一格,并且不能移出迷宫。搬起石头和放下石头不算步数。那么,从起点开始,我们最少需要多少步才能最后拿到宝藏呢?如果无法拿到宝藏,返回 -1 。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/xun-bao
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

猜你喜欢

转载自blog.csdn.net/qq_33831360/article/details/105773882
今日推荐