SP1702 CLEANRBT - Cleaning Robot POJ 2688 Cleaning Robot

洛谷 SP1702 CLEANRBT - Cleaning Robot

POJ 2688 Cleaning Robot

題目鏈接:洛谷 SP1702 CLEANRBT - Cleaning Robot

算法標籤: 動態規劃(DP)狀態壓縮

題目

題意翻譯

在這裏,我們要解決的問題是路徑規劃移動機器人清潔矩形房間地板。

考慮房間地板鋪的是方形瓷磚,其尺寸適合清潔機器人(1×1)。這裏有“乾淨瓷磚”和“髒瓷磚”,機器人可以把“髒瓷磚”變成“乾淨瓷磚”。也可能有一些佔一個瓷磚大小的障礙(傢俱)在房間裏。如果瓷磚上有障礙物,機器人就不能訪問它。機器人移動到一個相鄰的瓷磚一個移動。機器人移動的瓦片必須是與機器人所在的瓦片相鄰的四個瓦片(即東、西、北或南)之一。機器人可以訪問兩次或更多的瓦片。

你的任務是編寫一個程序,計算機器人的最小移動次數,如果可能的話,將所有的“髒瓷磚”改爲“乾淨瓷磚”。

題目描述

Here, we want to solve path planning for a mobile robot cleaning a rectangular room floor with furniture.

Consider the room floor paved with square tiles whose size fits the cleaning robot (1 × 1). There are 'clean tiles' and 'dirty tiles', and the robot can change a 'dirty tile' to a 'clean tile' by visiting the tile. Also there may be some obstacles (furniture) whose size fits a tile in the room. If there is an obstacle on a tile, the robot cannot visit it. The robot moves to an adjacent tile with one move. The tile onto which the robot moves must be one of four tiles (i.e., east, west, north or south) adjacent to the tile where the robot is present. The robot may visit a tile twice or more.

Your task is to write a program which computes the minimum number of moves for the robot to change all 'dirty tiles' to 'clean tiles', if ever possible.

輸入格式

IThe input consists of multiple maps, each representing the size and arrangement of the room. A map is given in the following format.

w h
c11 c12 c13 ... c1w
c21 c22 c23 ... c2w
...
ch1 ch2 ch3 ... chw

The integers w and h are the lengths of the two sides of the floor of the room in terms of widths of floor tiles. w and h are less than or equal to 20. The character cyx represents what is initially on the tile with coordinates (x, y) as follows.

'.' : a clean tile
'*' : a dirty tile
'x' : a piece of furniture (obstacle)
'o' : the robot (initial position)

In the map the number of 'dirty tiles' does not exceed 10. There is only one 'robot'.

The end of the input is indicated by a line containing two zeros.

輸出格式

For each map, your program should output a line containing the minimum number of moves. If the map includes 'dirty tiles' which the robot cannot reach, your program should output -1.

輸入輸出樣例

輸入 #1

7 5
.......
.o...*.
.......
.*...*.
.......
15 13
.......x.......
...o...x....*..
.......x.......
.......x.......
.......x.......
...............
xxxxx.....xxxxx
...............
.......x.......
.......x.......
.......x.......
..*....x....*..
.......x.......
10 10
..........
..o.......
..........
..........
..........
.....xxxxx
.....x....
.....x.*..
.....x....
.....x....
0 0

輸出 #1

8
49
-1

題解:

狀壓DP(簡化TSP問題) + BFS求最短路徑

題目大意:

m*n矩陣,有不超過10個需要走到的點,給出起點,以及一些障礙,問走最少的步子把所有點走完。n、m <= 20

對於整道題來講,看到給定的一張圖,首先我們能夠想到的就是用BFS(廣度優先搜索)找出任意兩個可行點(對於起點和所有要到達的‘*’點)之間的距離,那麼我們就對每一個可行點跑一遍BFS將\(dis[][]\)數組預處理出來,最終按照簡化的TSP問題,狀態壓縮求解即可。

對於這道題有以下需要注意的:

1.BFS的過程是否正確(可以手推)

2.對於狀態壓縮的狀態更新是否正確

3.由於計算\(dp[][]\)時候可能涉及到兩個\(inf\)相加而出現爆\(int\)的情況,所以對於\(ans,dp[][],dis[][]\)我們需要開\(long long\)

AC代碼

//#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>

using namespace std;

typedef long long ll;

const ll inf = 0x3f3f3f3f3f3f;
int n, m, cnt;
char str[25][25];
ll dis[25][25], dp[5050][25], vis[25][25];
int dir[4][2] = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} };
struct Point{
    int x, y;
}point[25];
void bfs(int s)
{
    queue <Point> q;
    Point cur, next;
    cur.x = point[s].x;
    cur.y = point[s].y;
    memset(vis, -1, sizeof vis);
    vis[cur.x][cur.y] = 0;
    q.push(cur);
    while (!q.empty())
    {
        cur = q.front();
        q.pop();
        for (int i = 0; i < 4; i ++ )
        {
            next.x = cur.x + dir[i][0];
            next.y = cur.y + dir[i][1];
            if (next.x < 0 || next.y < 0 || next.x >= n || next.y >= m)
                continue ;
            if (str[next.x][next.y] == 'x')
                continue ;
            if (vis[next.x][next.y] == -1)
            {
                vis[next.x][next.y] = vis[cur.x][cur.y] + 1;
                q.push(next);
                char ch = str[next.x][next.y];
                if (ch >= 'a' && ch < 'o')
                    dis[s][ch - 'a' + 1] = dis[ch -'a' + 1][s] = vis[next.x][next.y];
            }
        }
    }
}
int main()
{
    while(scanf("%d%d", &m, &n) != EOF)
    {
        if (n == 0 && m == 0)
            break;
        for (int i = 0; i < n; i ++ )
            scanf("%s", str[i]);
        cnt = 0;
        for (int i = 0; i < n; i ++ )
        {
            for (int j = 0; j < m; j ++ )
            {
                if (str[i][j] == 'o')
                {
                    point[0].x = i;
                    point[0].y = j;
                }
                else if (str[i][j] == '*')
                {
                    cnt ++ ;
                    str[i][j] = 'a' + cnt - 1;
                    point[cnt].x = i;
                    point[cnt].y = j;
                }
            }
        }
        if (cnt == 0)
        {
            printf("0\n");
            continue ;
        }
        memset(dis, 0x3f3f3f3f, sizeof dis);
    /*  for(int i=1;i<=cnt;i++ )
            for (int j = 1; j <= cnt; j ++ )
                dis[i][j] = inf;*/
        for (int i = 0; i < cnt; i ++ )
        {
            bfs(i);
            /*for (int j = 0; j <= cnt; j ++ )
            {
                if (dis[i][j] == -1)
                    dis[i][j] = inf;
            }*/
        }
        /*cout << endl << endl;
        for (int i = 1; i <= cnt; i ++ )
        {
            printf("%d ", dis[0][i]);
        }
        cout << endl;*/
        //memset(dp, -1, sizeof dp);
        //dp[0][0] = 0;
        int tot = (1 << cnt) - 1;
        for (int s = 0; s <= tot; s ++ )
        {
            for (int i = 1; i <= cnt; i ++ )
            {
                if (s & (1 << (i - 1)))
                {
                    if (s == (1 << (i - 1)))
                        dp[s][i] = dis[0][i];
                    else
                    {
                        dp[s][i] = inf;
                        for (int j = 1; j <= cnt; j ++ )
                        {
                            if (s & (1 << (j - 1)) && i != j)
                                dp[s][i] = min(dp[s][i], dp[s ^ (1 << (i - 1))][j] + dis[j][i]);
                        }
                    }
                }
            }
        }
        ll ans = inf;
        for (int i = 1; i <= cnt; i ++ )
            ans = min(ans, dp[tot][i]);
        if (ans >= 80000)
            printf("-1\n");
        else printf("%lld\n", ans);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章