HDU 1043 8×8デジタル(古典的な問題)
問題の意味
クラシックの問題は、もはや説明しないこと。
これは、あなたがそれを達する要求する必要があり、あなたの状態を与えるために主にある(1,2,3,4,5,6,7,8、X \)\転送経路を。
問題解決のためのアイデア
ソリューションの多くがありますが、私はここで本当に高齢者の説明を参照する、巨大な書き込みに良いを持っています!残念ながら、私は先輩>︿<を書いたか分かりません。
ここでは高齢者の問題の解決策は、独自の変更を書いて、少しの追加です。
クラシックの質問は、解決策がたくさんありますが、我々は最初にどのように多くの8つのデジタル状態の合計を計算します。デジタルエイトは9つの文字が含まれ、9つの文字は、ある任意の順序にすることができ(= 362880 \ 9!)\。唯一の8つのデジタル状態はそう\(36W \)ではなく、たくさん。
ストリームの知識は、次の3つの方法(達した状態を記録するか否か)を必要とします。
知識ポイント1:キャンター拡張(ここでは詳細には触れなくなった、特定の百度を参照してください)
私たちは、カントールの使用を開始することができ、自然数に変換を手配するために、我々は簡単に特定の状態に達したかどうかを知ることができるように、。
テーブルを再生する方法
我々は、から見ることができ、最終的な状態
[\ {始める[左\ \
\]}マトリックス1&2&3 \\ 4&5&6 \\ 7&8&X \\\端{行列} \右] に全ての他の状態検索し、パスを記録します。次にそれぞれの、頼む直接出力答えを(それが最初のテーブルを打つと同等である)に。
第2のストリームの知識は、次の2つの方法(状況解けるかどうかを決定するために使用される)の使用を必要とします。
知識ポイント2:\(N- \)デジタルの問題の可解性
デジタルゲーム2奇妙な状況まで、場合に限り、次の二つのシーケンシャル書き込み1ラインのグリッド状況の数\(N * N - 1 \ ) 逆の(スペースに関係なく)配列要素の後、数同じパリティ。例えば、情勢書かれた\([5,2,8,1,3,4,6,7] \) 。結論必要ときに空間アップ(ダウン)、それが交換位置の数の数N-1(前面)側に対応し、空間の周囲を移動明記不変配列:容易プルーフN-1が偶数であるので、逆の変更は偶数であることができます。結論の妥当性証明はもっと複雑である私たちがなり、長さでそのような数学の問題を、これを議論していません。
上記の結論はまた、に拡張することができる\(N- \)偶数である場合、状況には2つの状況まで、及び2つのグリッドが書き込まれた場合だけシーケンス対応し、「逆の数の差」と「」2 「同じパリティ次状況空白行の数の差。実際には、\(M \ N *)グリッドには(\(N、M \ GEQ 2 \)もの一つを受ける2つの結論は、列に従って(前記しましたパリティ部分)の議論
和、\(N-M * \)デジタル解ける問題の決意は、逆の順序を解決しようとするように変換することができます。
追加:実際には、以下の
デジタル** N * M:
mは奇数、逆に同じパリティの二つの状態です。
mが一致「の列の間のスペースの数に逆+差の」偶数、二つの状態のパリティです。**
次の2つの方法が最初の可解性の状況で判断しなければならない唯一のケースの可解性の検索、。そうでない場合は、解決策のない状況は、関係なく、検索プログラムがどうなるかの場合には、状態空間の完全な検索を完了、あること、でしょうタイムアウト。
BFS 2つの双方向の方法(この問題の解決策は、その後のピットを作る準備ができて、コードではありません)
あなたがテーブルにヒットしない場合、我々は複雑さを軽減するために、双方向のBFSを使用することができます(番号ための層数と一緒に検索ツリーノード指数関数的成長が)。
被験者に双方向状態と最終状態を交互にBFS、キューノードのヘッドが既に別のブランチを訪れたら取らこの状態で見つかった、 2人のBFSの出会い、再帰出力回答を記載しています。
方法3 A *ヒューリスティック検索
状況評価関数:でマンハッタン距離の最終位置の各値の現在の位置全てのコスト値の各値の料金として合計、コードの現在の状況があります。
ヒューリスティック探索は、評価関数に従って行うことができます。
コードの実装(方法3:A *アルゴリズム)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
struct node{
int s[9];
int cur, n;
int f, g, h;
//注意,因为优先队列原本是从大到小的排列,所以我们需要重写小于号,这里的小于的功能实际是大于号的功能
//这样优先队列里面排序才是从小到大进行的排序。
//我的实现方式是用友元函数重载小于运算符
friend bool operator <(const node a, const node b)
{
return a.f==b.f ? a.g > b.g : a.f > b.f;
}
//下面是学长写的小于号运算符重载形式,使用的是成员函数运算符重载
//bool operator<(const node &a) const {
// return a.f == f ? a.g < g : a.f < f;
//}
};
const char dir[]="dulr";
const int nexts[][2]={{1, 0}, {-1, 0}, {0, -1}, {0, 1}};
const int maxn=363000;
int fac[]={1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880}; //0-9的阶乘
bool vis[maxn];
int pre[maxn];
char path[maxn];
int cantor(const int s[])
{
int sum=0;
for(int i=0; i<9; i++)
{
int cnt=0;
for(int j=i+1; j<9; j++)
if(s[j] < s[i])
cnt++;
sum+=cnt*fac[9-i-1];
}
return sum;
}
int getval(const int *s) //曼哈顿距离
{
int val=0;
int x, y, tx, ty;
for(int i=0; i<9; i++)
{
x=i/3; y=i%3;
tx=(s[i]-1)/3; ty=(s[i]-1)%3;
val+=abs(x-tx)+abs(y-ty);
}
return val;
}
void printans(int n)
{
if(pre[n])
{
printans(pre[n]);
printf("%c", path[n]);
}
}
void AStar(node s)
{
memset(vis, false, sizeof(vis));
priority_queue<node> q;
q.push(s);
vis[s.n]=true;
pre[s.n]=0;
int ans=0;
while(!q.empty())
{
node now=q.top();
q.pop();
if(now.n==ans)
{
printans(ans);
printf("\n");
return ;
}
int x=now.cur/3, y=now.cur%3;
for(int p=0; p<4; p++)
{
int tx=nexts[p][0]+x, ty=nexts[p][1]+y;
if(tx<0 || ty<0 || tx>2 || ty>2) continue;
node tmp=now;
tmp.cur=tx*3+ty;
swap(tmp.s[now.cur], tmp.s[tmp.cur]);
tmp.n=cantor(tmp.s);
if(vis[tmp.n]) continue;
vis[tmp.n]=true;
pre[tmp.n]=now.n;
path[tmp.n]=dir[p];
tmp.g++;
tmp.h=getval(tmp.s);
tmp.f=tmp.g+tmp.h;
q.push(tmp);
}
}
}
int main()
{
char ch;
node s;
while(scanf(" %c", &ch) !=EOF)
{
if(ch=='x')
ch='9', s.cur=0;
s.s[0]=ch-'0';
for(int i=1; i<9; i++)
{
scanf(" %c", &ch);
if(ch=='x')
ch='9', s.cur=i;
s.s[i]=ch-'0';
}
s.n=cantor(s.s);
s.g=0;
s.h=getval(s.s);
s.f=s.g+s.h;
int cnt=0;
for(int i=0; i<8; i++)
{
if(s.s[i]!=9)
for(int j=i+1; j<9; j++)
if(s.s[j] < s.s[i])
cnt++;
}
if(cnt&1)
printf("unsolvable\n");
else AStar(s);
}
return 0;
}