您的位置:首页 > 其它

[kuangbin带你飞]专题二 搜索进阶 题解(康托展开、映射、迭代加深)

2017-12-01 15:22 525 查看
专题链接

A:Eight (康托展开)

经典的八数码问题,这题直接bfs是不行的,时限不够,主要是在每个状态判重的时候使用map,在这里的复杂度可以通过康托展开来达到查重O(1),那么!什么是康托展开呢?!

我觉得这个博客写的很好,总之康托展开用来看当前状态中在全排列中是第几个,可以把问题的全部状态不用map来表示

这题hdu是多组样例输入,poj则单组。

本来网上写做这题要有八境界,把代码写八遍。我就算了吧。。。

poj AC版本:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <vector>
#include <map>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn = 10;
const int N = 1e6+10;
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};
char way[4] = {'r','l','d','u'};
int fac[10] = {1,1,2,6,24,120,720,5040,40320};
char killme[362890][50];
bool vis[362890];
int s,e;
int getHash(int arr[]){
int i,j,k;
int sum = 0,num;
int p = 9;
for(i=0;i<9;i++){
--p;
num = 0;
for(j=8;j>i;j--){
if(arr[j] < arr[i]){
++num;
}
}
sum += num*fac[p];
}
return sum;
}

void reverse_kangtuo(int n,int k,int arr[]){
int i, j, t, vst[10]={0};
for (i=0; i<n; i++){
t = k/fac[n-i-1];
for (j=1; j<=n; j++){
if (!vst[j]){
if (t == 0) break;
--t;
}
}
arr[i] = j;
vst[j] = 1;
k %= fac[n-i-1];
}
}

//map<long long, bool> vis;
queue<int> que;
bool flag;
void bfs(){
int i,j,nx,ny;
int now[9];
while(!que.empty()){
que.pop();
}
memset(vis, 0, sizeof(vis));
que.push(s);
while(!que.empty()){
int q = que.front();
que.pop();
vis[q] = true;
if(q == e){
break;
}
reverse_kangtuo(9, q, now);
int x,y;
for(i=0;i<9;i++){
if(now[i] == 9){
x = i/3;
y = i%3;
}
}
int len = strlen(killme[q]);
for(i=0;i<4;i++){
nx = x + dx[i];
ny = y + dy[i];
if(0<=nx&&nx<3 && 0<=ny&&ny<3){
swap(now[nx*3 + ny], now[x*3 + y]);
int code = getHash(now);
if(!vis[code]){
que.push(code);
strcpy(killme[code], killme[q]);
killme[code][len] = way[i];
killme[code][len+1] = '\0';
}
swap(now[nx*3 + ny], now[x*3 + y]);
}

}
}
}
char in[20];
int ask[9];
void init(){
killme[0][0] = '\0';
memset(vis, false, sizeof(vis));
bfs();
}
int main(){
int i,j,p;
//init();
e = 0;
while(gets(in) != NULL){
p = -1;
int len = strlen(in);
for(i=0;i<len;i++){
if(in[i] != ' '){
++p;
if(in[i] == 'x'){
ask[p] = 9;
}else{
ask[p] = in[i] - '0';
}
}
}
s = getHash(ask);
killme[s][0] = '\0';
memset(vis, false, sizeof(vis));
bfs();
if(vis[e]){
//            int len = strlen(killme[index]);
//            for(i=len-1;i>=0;i--){
//                printf("%c",killme[index][i]);
//            }
printf("%s\n",killme[e]);
}else{
printf("unsolvable\n");
}
}

return 0;
}


Hdu AC版本:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <vector>
#include <map>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn = 10;
const int N = 1e6+10;
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};
char way[4] = {'l','r','u','d'};
int fac[10] = {1,1,2,6,24,120,720,5040,40320};
char killme[362890][50];
bool vis[362890];
int getHash(int arr[]){
int i,j,k;
int sum = 0,num;
int p = 9;
for(i=0;i<9;i++){
--p;
num = 0;
for(j=8;j>i;j--){
if(arr[j] < arr[i]){
++num;
}
}
sum += num*fac[p];
}
return sum;
}

void reverse_kangtuo(int n,int k,int arr[]){
int i, j, t, vst[10]={0};
for (i=0; i<n; i++){
t = k/fac[n-i-1];
for (j=1; j<=n; j++){
if (!vst[j]){
if (t == 0) break;
--t;
}
}
arr[i] = j;
vst[j] = 1;
k %= fac[n-i-1];
}
}

//map<long long, bool> vis;
queue<int> que;
bool flag;
void bfs(){
int i,j,nx,ny;
int now[9];
while(!que.empty()){
que.pop();
}
memset(vis, 0, sizeof(vis));
que.push(0);
while(!que.empty()){
int q = que.front();
que.pop();
vis[q] = true;
reverse_kangtuo(9, q, now);
int x,y;
for(i=0;i<9;i++){
if(now[i] == 9){
x = i/3;
y = i%3;
}
}
int len = strlen(killme[q]);
for(i=0;i<4;i++){
nx = x + dx[i];
ny = y + dy[i];
if(0<=nx&&nx<3 && 0<=ny&&ny<3){
swap(now[nx*3 + ny], now[x*3 + y]);
int code = getHash(now);
if(!vis[code]){
que.push(code);
strcpy(killme[code], killme[q]);
killme[code][len] = way[i];
killme[code][len+1] = '\0';
}
swap(now[nx*3 + ny], now[x*3 + y]);
}

}
}
}
char in[20];
int ask[9];
void init(){
killme[0][0] = '\0';
memset(vis, false, sizeof(vis));
bfs();
}
int main(){
int i,j,p;
init();
while(gets(in) != NULL){
p = -1;
int len = strlen(in);
for(i=0;i<len;i++){
if(in[i] != ' '){
++p;
if(in[i] == 'x'){
ask[p] = 9;
}else{
ask[p] = in[i] - '0';
}
}
}
int index = getHash(ask);
if(vis[index]){
int len = strlen(killme[index]);
//cout<<"len "<<len<<endl;
for(i=len-1;i>=0;i--){
printf("%c",killme[index][i]);
}
printf("\n");
}else{
printf("unsolvable\n");
}
}

return 0;
}


B - Eight II

想法和A一样打表,根据X起始位置不同打9个表。其他数字通过映射来达成一致。之前用queue超时了。于是手写队列。

wa了很久是因为bfs写挂了,在新点入队的时候就应该标记它,切忌出队标记!!!

代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <vector>
#include <map>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn = 362885;
int dx[4] = {1,0,0,-1};
int dy[4] = {0,-1,1,0};
char way[4] = {'d','l','r','u'};
int fac[10] = {1,1,2,6,24,120,720,5040,40320,362880};
char killme[9][maxn];
int last[9][maxn];
int pos9[maxn];
int findHead[9][maxn];
bool vis[9][maxn];
int tot;
int s;
int getHash(int arr[]){
int i,j,t,sum;
sum=0;
for( i=0; i<9 ;++i)
{
t=0;
for(j=i+1;j<9;++j)
if( arr[i]>arr[j] )
++t;
sum+=t*fac[9-i-1];
}
return sum + 1;
}
bool vst[10];
void reverse_kangtuo(int n,int k,int arr[]){
int i, j, t;
--k;
bool vst[10];
memset(vst, 0, sizeof(vst));
for (i=0; i<n; i++){
t = k/fac[n-i-1];
for (j=1; j<=n; j++){
if (!vst[j]){
if (t == 0) break;
--t;
}
}
arr[i] = j;
vst[j] = 1;
k %= fac[n-i-1];
}
}
int que[maxn], qs, qe;
int nowArr[9];
void bfs(int index){
memset(vis[index], false, sizeof(vis[index]));
int i, nx, ny, x, y, code, q, lastTot, now, next;
findHead[index][s] = 0;
last[index][s] = 0;
vis[index][s] = true;
que[qe] = s;
qe = (qe+1)%maxn;
while(qe-qs>0){
q = que[qs];
qs = (qs + 1)%maxn;
reverse_kangtuo(9, q, nowArr);
x = pos9[q]/3 ,y = pos9[q]%3;
lastTot = findHead[index][q];
for(i=0;i<4;i++){
nx = x + dx[i];
ny = y + dy[i];
if(0<=nx&&nx<3 && 0<=ny&&ny<3){
next = nx*3 + ny;
now = x*3 + y;
swap(nowArr[next], nowArr[now]);
code = getHash(nowArr);
if(!vis[index][code]){
pos9[code] = next;
vis[index][code] = true;
que[qe] = code;
qe = (qe+1)%maxn;
killme[index][++tot] = way[i];
last[index][tot] = lastTot;
findHead[index][code] = tot;
}
swap(nowArr[next], nowArr[now]);
}

}
}
}
char in[20];
void init(){
int now[9];
int p;
for(int i=0;i<9;i++){
p = 1;
for(int j=0;j<9;j++){
if(j==i) now[j] = 9;
else now[j] = p++;
}
s = getHash(now);
pos9[s] = i;
tot = 0;
qs = 0, qe = 0;
bfs(i);
}
}
int change[10];
char comeTo[10];
int main(){
int i,rnd=1;
init();
int t;
scanf("%d",&t);
while(t--){
scanf("%s%s",in, comeTo);
int xPos;
int j = 1;
for(i=0;i<9;i++){
if(in[i] == 'X'){
xPos = i;
}else{
change[in[i] - '0'] = j++;
}
}
int mapping[9];
for(i=0;i<9;i++){
if(comeTo[i] == 'X'){
mapping[i] = 9;
}else{
mapping[i] = change[comeTo[i] - '0'];
}
}
int e = getHash(mapping), fucker = 0;
char motherFucker[30];
int p = findHead[xPos][e];
while(p!=0){
motherFucker[++fucker] = killme[xPos][p];
p = last[xPos][p];
}
printf("Case %d: %d\n",rnd++,fucker);
for(i=fucker;i>0;i--){
printf("%c", motherFucker[i]);
}
printf("\n");
}
return 0;
}


C:哈密顿绕行世界问题

正常的dfs,每个点的可达点排个序会好写一些

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <vector>
#include <map>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn = 30;
int mp[maxn][5];
int vis[maxn],s,tot;
int way[maxn];
void dfs(int p, int index){
int i;
if(index > 20){
if(way[20]!=s) return;
printf("%d:  %d ",++tot,s);
for(i=1;i<=20;i++){
printf("%d%c",way[i],i==20?'\n':' ');
}
return;
}
for(i=1;i<=3;i++){
if(vis[mp[p][i]]){
--vis[mp[p][i]];
way[index] = mp[p][i];
dfs(mp[p][i], index+1);
++vis[mp[p][i]];
}
}
}
int main(){
int i;
for(i=1;i<=20;i++){
scanf("%d%d%d",&mp[i][1],&mp[i][2],&mp[i][3]);
sort(mp[i] + 1, mp[i]+1+3);
}
while(~scanf("%d",&s) && s){
for(i=1;i<=20;i++){
vis[i] = 1;
}
tot = 0;
dfs(s, 1);
}
return 0;
}


D - Escape

预处理一下地图,dis这个数组应该是[time][x][y]这样的三维数组,表示某个点在某时不能走。处理子弹的时候要注意子弹飞行路径上只要有城堡子弹就被挡住了。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <map>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn = 110;
const int inf = 0x3f3f3f3f;
int dx[5] = {0,0,1,-1,0};
int dy[5] = {1,-1,0,0,0};
int direction[maxn];//right, left, down, up
bool isCastle[maxn][maxn];
bool killed[maxn][maxn][1010];
bool dis[maxn][maxn][1010];
pair<int, int> castles[maxn];
int n,m,k,d;
pair<int,int> shoot[maxn];//first:cycle second:speed
struct unit{
pair<int, int> pos;
int step;
};
void init(){
int i,j,p,x,y;
memset(killed, false, sizeof(killed));
for(i=1;i<=k;i++){
for(j=0;j<=d;j+=shoot[i].first){//shoot time
p = 1;
while(1){
x = castles[i].first + dx[direction[i]]*p;
y = castles[i].second + dy[direction[i]]*p;
if(0<=x&&x<=n && 0<=y&&y<=m && !isCastle[x][y]){
if(p%shoot[i].second==0){
//cout<<x<<" "<<y<<" "<<p<<endl;
killed[x][y][j+p/shoot[i].second] = true;
//cout<<x<<" "<<y<<" "<<j+p/shoot[i].second<<" be killed"<<endl;
}
}else{
break;
}
++p;
}

}
}
}
queue<unit> que;
int bfs(){
memset(dis, false, sizeof(dis));
int i,j,nx,ny;
while(!que.empty()){
que.pop();
}
dis[0][0][0] = true;
unit s,next;
pair<int,int> e;
e.first = n;
e.second = m;
s.pos.first = 0;
s.pos.second = 0;
s.step = 0;
que.push(s);
while(!que.empty()){
unit q = que.front();
que.pop();
//cout<<q.pos.first<<" "<<q.pos.second<<" "<<q.step<<endl;
if(q.step > d){
continue;
}
if(q.pos == e){
return q.step;
}
for(i=0;i<5;i++){
nx = q.pos.first + dx[i];
ny = q.pos.second + dy[i];
if(0<=nx&&nx<=n && 0<=ny&&ny<=m && !isCastle[nx][ny] && !killed[nx][ny][q.step+1] && !dis[nx][ny][q.step+1]){
next.pos.first = nx;
next.pos.second = ny;
next.step = q.step + 1;
dis[nx][ny][q.step+1] = true;
que.push(next);
}
}
}
return -1;
}
int main(){
int i,j,x,y;
char in[2];
while(~scanf("%d%d%d%d",&n,&m,&k,&d)){
memset(isCastle, false, sizeof(isCastle));
for(i=1;i<=k;i++){
scanf("%s",in);
if(in[0] == 'N'){
direction[i] = 3;
}else if(in[0] == 'S'){
direction[i] = 2;
}else if(in[0] == 'W'){
direction[i] = 1;
}else{
direction[i] = 0;
}
scanf("%d%d%d%d", &shoot[i].first, &shoot[i].second, &castles[i].first, &castles[i].second);
isCastle[castles[i].first][castles[i].second] = true;
}
init();
int res = bfs();
if(res == -1){
printf("Bad luck!\n");
}else{
printf("%d\n",res);
}

}
return 0;
}


E - DNA sequence(迭代加深)

这题求题目给的n个DNA序列的最短公共母序列的长度。迭代加深的意思就是每次搜索都固定搜索的深度,如果超过了搜索深度就不搜了,慢慢加深

搜索过程有个剪枝是,如果当前任意串未匹配的长度还是加上当前深度大于限制的最深深度的话,就不搜了。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <vector>
#include <map>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn = 10;
char save[maxn][maxn];
char rigi[maxn] = {'A','C','G','T'};
int pos[maxn],len[maxn];
int n,maxDeep,res;
void copyy(int now[]){
int i;
for(i=1;i<=n;i++){
now[i] = pos[i];
}
}
void reduction(int now[]){
int i;
for(i=1;i<=n;i++){
pos[i] = now[i];
}
}
void dfs(int index){
int i,j;
if(index > maxDeep){
return;
}
int last;
bool allZero = true;
//cout<<"maxDeep "<<maxDeep<<endl;
for(i=1;i<=n;i++){
//cout<<pos[i]<<" ";
last = len[i] - pos[i];
if(last != 0){
allZero = false;
}
if(index + last > maxDeep){
return;
}
}
//cout<<endl;
if(allZero){
res = maxDeep;
}
if(res != -1){
return;
}
int nowPos[maxn];
copyy(nowPos);
for(i=0;i<4;i++){
bool flag = false;
for(j=1;j<=n;j++){
if(save[j][pos[j]] == rigi[i]){
flag = true;
pos[j] = pos[j] + 1;
}
}
if(flag){
dfs(index+1);
reduction(nowPos);
}
if(res != -1){
return;
}
}
}
int main(){
int t,i,j;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
maxDeep = 0;
for(i=1;i<=n;i++){
scanf("%s",save[i]);
len[i] = strlen(save[i]);
maxDeep = max(maxDeep, len[i]);
}
res = -1;
while(res == -1){
memset(pos, 0, sizeof(pos));
dfs(0);
maxDeep += 1;
}
printf("%d\n",res);
}
return 0;
}


I:A计划

要求在T时刻找到公主,我以为是正正好要T的时候到达终点。。。。。写了dfs超时。。。。结果只要最短时间<=T即可。。。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <vector>
#include <map>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn = 20;
const int inf = 0x3f3f3f3f;
char save[2][maxn][maxn];
struct unit{
int x,y,z;
}s,e;
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};
int n,m,dis[2][maxn][maxn],t;
bool flag;
int bfs(){
int i,j,k;
queue<unit> que;
for(i=0;i<2;i++){
for(j=0;j<n;j++){
for(k=0;k<m;k++){
dis[i][j][k] = inf;
}
}
}
que.push(s);
dis[s.x][s.y][s.z] = 0;
while(!que.empty()){
unit p = que.front();
que.pop();
if(p.x==e.x && p.y==e.y && p.z==e.z){
break;
}
for(i=0;i<4;i++){
int nx = p.y + dx[i];
int ny = p.z + dy[i];
if(0<=nx && nx<n && 0<=ny && ny<m && (save[p.x][nx][ny] == '.' || save[p.x][nx][ny]=='P') && dis[p.x][nx][ny] > dis[p.x][p.y][p.z] + 1){
unit now;
now.x = p.x;
now.y = nx;
now.z = ny;
que.push(now);
dis[p.x][nx][ny] = dis[p.x][p.y][p.z] + 1;
}
if(0<=nx && nx<n && 0<=ny && ny<m && save[p.x][nx][ny] == '#'  && (save[!p.x][nx][ny] == '.' || save[!p.x][nx][ny]=='P') && dis[!(p.x)][nx][ny] > dis[p.x][p.y][p.z] + 1){
unit now;
now.x = !(p.x);
now.y = nx;
now.z = ny;
que.push(now);
dis[!(p.x)][nx][ny] = dis[p.x][p.y][p.z] + 1;
}
}
}
return dis[e.x][e.y][e.z];
}
void dfs(unit p, int time){
//cout<<p.x<<" "<<p.y<<" "<<p.z<<" "<<time<<endl;
if(time > t || flag){
return;
}
if(time == t && p.x==e.x && p.y==e.y && p.z==e.z){
flag = true;
return;
}
int i,j;
for(i=0;i<4;i++){
int nx = p.y + dx[i];
int ny = p.z + dy[i];
unit now;
if(0<=nx && nx<n && 0<=ny && ny<m && (save[p.x][nx][ny] == '.' || save[p.x][nx][ny]=='P')){
now.x = p.x;
now.y = nx;
now.z = ny;
dfs(now, time+1);
}
if(0<=nx && nx<n && 0<=ny && ny<m && save[p.x][nx][ny] == '#'  && save[!(p.x)][nx][ny]=='.'){
now.x = !(p.x);
now.y = nx;
now.z = ny;
dfs(now, time+1);
}
}
}
int main(){
int c,i,j,k;
scanf("%d",&c);
while(c--){
scanf("%d%d%d",&n,&m,&t);
for(j=0;j<2;j++){
for(i=0;i<n;i++){
scanf("%s", save[j][i]);
int len = strlen(save[j][i]);
for(k=0;k<len;k++){
if(save[j][i][k] == 'S'){
s.x = j;
s.y = i;
s.z = k;
}
if(save[j][i][k] == 'P'){
e.x = j;
e.y = i;
e.z = k;
}
}
}
}
flag = false;
//dfs(s, 0);
//cout<<bfs()<<endl;
if(bfs()<=t){
printf("YES\n");
}else{
printf("NO\n");
}
/*if(flag){
printf("YES\n");
}else{
printf("NO\n");
}*/
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  搜索