背景
今日羅谷を訪れたときにこの質問に気づきました。元の質問は [P1002 [NOIP2002 Popularization Group] Crossing the River - Luogu | New Ecology of Computer Science] に関連しています。教育 (luogu.com.cn)]
チェスが大好きな私にとって、それはもちろん必須の質問です。
質問の意味
ポーンの開始点は左上隅 (0,0) にあり、プログラムは 2 つの座標を受け取ります。ポーンのターゲット ポイントの右下隅 (end_x、end_y) と、敵の馬の座標点 ( horse_x、horse_y)。
このうちポーンは右か下に1マスしか移動できず、ナイトの足元や位置を通過することはできず、最大9点を通過することはできません。
左上隅から右下隅までに解がいくつあるかを求めます。データボリュームの合意された最大座標値は 20 を超えてはなりません。
解析する
一見すると、これは再帰的アルゴリズムの試験問題であることがわかります。この問題では、対象点の解の数を表す解の公式は次のようになります。
res(end_x,end_y)=res(end_x-1,end_y)+res(end_x,end_y-1)
ここで、res(0,0)=1
この問題を効果的に解決するために、(0,0) から開始し、キューの先入れ先出しデータ構造を使用します。
1 つのポイントが 2 つのポイントによって到着する可能性があるため、重複を削除するには、フラグ配列 flag と double queue を使用して、各ポイントが最大 1 回だけキューに入れられるようにする必要があります。
敵馬の存在を考慮して、馬の足元と馬の位置にマイナス値を追加します。これらのマイナス値の位置を通過するたびにチームに加わりません。そうでない場合は、チームに加わり、この位置をマークします. 次回このポジションを検索するときは、チームに参加し続けるプランの数のみを追加します。
キューが完全に空になると、res(end_x,end_y) という結果が出力されます。
時間計算量 O(n^2)、空間計算量 O(n^2)
コード
#include<bits/stdc++.h>
using namespace std;
class point {
public:
int x,y;
};
int main() {
int start_x=0,start_y=0;
int horse_x,horse_y;
int end_x,end_y;
cin>>end_x>>end_y>>horse_x>>horse_y;
long long res[21][21]= {0};
res[0][0]=1;
bool flag[21][21]= {0};
int horse_direct[][2]= {
{1,2},{1,-2},{-1,2},{-1,-2},{2,1},{2,-1},{-2,1},{-2,-1}
};
res[horse_x][horse_y]=-1;
for(int k=0; k<8; k++) {
int x=horse_x+horse_direct[k][0];
int y=horse_y+horse_direct[k][1];
if(x>=0&&y>=0&&x<=end_x&&y<=end_y) {
res[x][y]=-1;
}
}
int soldier_direct[][2]= {
{0,1},{1,0}};
queue<point> q;
q.push({start_x,start_y});
while(!q.empty()) {
queue<point> nq;
memset(flag,0,sizeof(flag));
while(!q.empty()) {
point p=q.front();
q.pop();
for(int k=0; k<2; k++) {
int x=p.x+soldier_direct[k][0];
int y=p.y+soldier_direct[k][1];
if(x<0||y<0||x>end_x||y>end_y) {
continue;
}
if(res[x][y]<0) {
continue;
}
res[x][y]+=res[p.x][p.y];
if(!flag[x][y]) {
nq.push({x,y});
flag[x][y]=1;
}
}
}
q=nq;
}
cout<<res[end_x][end_y]<<endl;
return 0;
}