///////////////////////////////////////////////////////////////////////////////////////////////////////
作者:tt2767
聲明:本文遵循以下協議自由轉載-非商用-非衍生-保持署名|Creative Commons BY-NC-ND 3.0
查看本文更新與討論請點擊:http://blog.csdn.net/tt2767
鏈接被刪請百度: CSDN tt2767
///////////////////////////////////////////////////////////////////////////////////////////////////////
題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=4771
這題從中午做到晚上啊,收穫還是比較多的。。。
學到的幾點:
1.狀態壓縮:把狀態壓成二進制,通過查看二進制的每一位來判斷狀態。
2.帶狀態的bfs,雖然原來做過一道,但之前是還是不太明白
3.把地圖數據化表示很有用
4.自己包裝輸入
5.。。。。。。。。。。
題解:
先把輸入的數據處理一下,記錄起點之後,數據化地圖:
牆爲-1,寶藏爲1,2,4,8(二進制就是0001,0010,0100,1000),起點和路都爲0.
每次通過加和記錄已找到寶藏的狀態
eg:找到了2,8記錄爲10(0010+1000=1010)
可用按位與來判斷是否撿過寶藏
寶藏可能在任何地方,如果在牆裏面,直接輸出-1;
#include<string>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
using namespace std;
typedef long long int LL;
const int INF = 0x3f3f3f3f ;
const long double PI = acos(0.0) * 2.0;
const int N = 200;
const int dx[]={0,1,0,-1};
const int dy[]={1,0,-1,0};
const int change[] ={0,1,2,4,8};
struct P
{
int x,y,step,num;
P(int x,int y,int step,int num):x(x),y(y),step(step),num(num){}
P(){}
};
P s;
bool vis[N][N][N];
char input[N][N];
int maze[N][N];
int sum,n,m;
bool flag;//是否有寶物在牆裏?
int bfs();
bool pass(P);//不管走沒走過,這個位置是可以走的
bool read();
int main()
{
while( read() )
printf("%d\n",bfs());
return 0;
}
int bfs()
{
if(flag) return -1; //如果在牆裏是不可能找全的
memset(vis,0,sizeof(vis));
queue<P> q;
vis[s.x][s.y][s.num] = 1;
q.push(s);
while(!q.empty())
{
P p = q.front();q.pop();
if(p.num == sum ) return p.step;
for(int i = 0 ; i < 4 ; i++)
{
P now = p; //先繼承上一次的屬性
now.x += dx[i];
now.y += dy[i];
if( pass(now) )
{
if(maze[now.x][now.y] > 0 ) // 本題關鍵步驟!!
{
int mark1 = maze[now.x][now.y]; //寶藏的標記
int mark2 = now.num; //人的標記
if( (mark1&mark2) == 0 ) //按位與判斷,如果沒撿過此寶藏,就把它撿起來
{
now.num += maze[now.x][now.y];
}
}
if(!vis[now.x][now.y][now.num])
{
now.step = p.step + 1;
vis[now.x][now.y][now.num] = 1;
q.push(now);
}
}
}
}
return -1;
}
bool read()
{
memset(input,'\0',sizeof(input));
memset(maze,0,sizeof(maze));
if(scanf("%d%d",&n,&m) != 2 || !(n+m)) return false;
for(int i = 1 ; i <= n ; i++)
{
getchar();
for(int j = 1 ; j <= m ; j++)
{
scanf("%c",&input[i][j]);
if(input[i][j] == '.') //地圖數據化
{
maze[i][j] = 0;
}
else if(input[i][j] == '@')
{
maze[i][j] = 0;
s = P(i,j,0,0);
}
else
{
maze[i][j] = -1;
}
}
}
sum = 0;
flag = false;
int k;
scanf("%d",&k);
for(int i = 1 ; i <= k ; i++)
{
int x,y;
scanf("%d%d",&x,&y);
if(maze[x][y] < 0) flag = true; //在牆裏的話標記一下
maze[x][y] += change[i];
sum+= maze[x][y];
}
return true;
}
bool pass(P z)
{
return (0 < z.x && z.x<= n && 0 < z.y && z.y <= m && maze[z.x][z.y] != -1 );
}