[kuangbin帶你飛]專題二 搜尋進階 題解(康託展開、對映、迭代加深)
阿新 • • 發佈:2019-01-29
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;
}