JakeLin- [Blue Bridge Cup]チェッカーボード多項式問題解決

8人の女王の問題は、女王をチェス盤に置き、お互いを攻撃して解決策を模索しないことです。駒を交換すると、8台の車、8頭の馬、8頭の兵士、8頭の王の問題も発生します。この問題では、チェスの駒が車に交換され、チェス盤を交換する必要がありましたが、正確にはいくつかの変更が加えられました。たとえば、現在n * nのチェス盤があります。いくつかのグリッドでいくつかの穴を選びます。当然、これらの穴はチェスの駒には使用できず、見落とされます。さらに、車はその仲間と攻撃した可能性があります。さて、考えてみれば、攻撃中に穴を踏むと自分を破壊してしまいます。したがって、車の攻撃範囲は穴で終わります。 
この質問では、チェス盤のスケールnと掘削状況を示します。k台の車を配置するための計画の数を見つけます(kから0から配置可能な最大の車の数)。 

データのスケールと規則 
n <= 8 

入力

最初の行の整数nは、チェス盤のサイズを示します。 
次のn行。各行には、スペースで区切られた0または1のn個の数字があります。0の形状は穴を示し、1は穴がないことを示します。 

アウトプット

複数の行、i番目の行はi台の車を配置する計画の数を表します 

入力例

3
1 0 1
1 1 1
1 0 1

出力例

7
12
4

タイトルリンク(提出可能)

タイトルが示すように、この質問は「nクイーンの質問」という遡及的方法の古典的な質問に基づいてい ます。この質問を深く理解する前に、 nクイーンの質問」「Nクイーン」の理解を深めるために前の記事を読むことを強くお勧めします。   そして、2Nクイーン問題分析」


I. nクイーンの問題の簡単な紹介

上記の記事でstep変数をご覧になりましたか?これは、ステップクイーンの配置を参照しています。また、ステップラインでのクイーンの配置も参照しています。

nクイーンの問題はnクイーンにしか入れることができず、各行に1つ、各列に1つしかありません。

ステップを決定したら、この行の各列を反復処理して、条件を満たす位置を見つけます。


次に、この質問はnクイーンの問題とどのように異なりますか

質問:さて、攻撃中に穴を踏むと、自分を破壊します。したがって、車の攻撃範囲は穴で終わります。
例:ラインは1 0 1

この行の最初の列が車を落とした場合、車の攻撃範囲は2列目の穴で終了するため、3番目の列は攻撃されません。
だから:

Q:ポイント(i、j)が訪問されたことがわかったら、次の場所を見つけるためにどこに行かなければなりませんか?
回答:はい、適格な位置を見つけるには、(i、j)の後のすべてのポイントをトラバースする必要があります同じ列を使用することもできます

Q:資格のあるポジションとは何ですか?
回答:
同じ行の位置が(x、y)であると想定します:(x、1〜y)に1があり、それらの間に0がない場合、攻撃されます。安全な位置が
同じ列にある場合でも、1 in(1〜x、y)があり、それらの間に0のブロックがない場合は、正当な位置ではなく攻撃され、そうでなければ安全な位置も

ここでは、特定のポイント(x、y)が安全かどうかを判断するチェック()を記述します。

bool check(int x,int y,int n) {
    for(int i=x-1; i>=1; i--) {
        if(map[i][y]==0) break; //如果在遇到1之前遇到0,那么它是安全的 
        if(vis[i][y]==1) return false; //如果还没遇到0就遇到了1,那就它是危险的 
    }
    for(int i=y-1; i>=1; i--) {
        if(map[x][i]==0) break;
        if(vis[x][i]==1) return false;
    }
    return true;
}

3、達成する方法

nクイーン問題と同様に、プロセスは変数を説明するDFSアルゴリズムを使用して実装されます。

dfs(int x,int y,int t,int n) // 访问了(x,y);此时t个车;n为棋盘规模
int num[maxn] // num[i]表示i个车的方案数

DFSアルゴリズムプロセスでは、特定の分析がコードに記述されています。

void dfs(int x,int y,int t,int n) {
    if(t<=n*n) num[t]++;
    for(int i=x; i<=n; i++) {  //遍历 (x,y)及其之后的所有点,从(1,1)开始
        int j;  // 遍历:x行y列之后的+下面所有行
        if(i==x) j=y;
        else j=1;
        for(;j<=n; j++) {
            if(vis[i][j]==0 && map[i][j]==1 && check(i,j,n)) { //满足条件的访问:没访问过 && 是陆地 && 不被攻击
                vis[i][j]=1;
                dfs(i,j,t+1,n);
                vis[i][j]=0;  // 回溯到DFS前的状态
            }
        }
    }
}

完全なコードは次のとおりです。 

#include<cstdio>
#include<iostream>
using namespace std;
const int maxn = 10;
int map[maxn][maxn];
int vis[maxn][maxn];
int num[maxn];
bool check(int x,int y,int n) {
    for(int i=x-1; i>=1; i--) {
        if(map[i][y]==0) break; //如果在遇到1之前遇到0,那么它是安全的 
        if(vis[i][y]==1) return false; //如果还没遇到0就遇到了1,那就它是危险的 
    }
    for(int i=y-1; i>=1; i--) {
        if(map[x][i]==0) break;
        if(vis[x][i]==1) return false;
    }
    return true;
}
void dfs(int x,int y,int t,int n) {
    if(t<=n*n) num[t]++;
    for(int i=x; i<=n; i++) {
        int j;
        if(i==x) j=y;
        else j=1;
        for(;j<=n; j++) {
            if(vis[i][j]==0 && map[i][j]==1 && check(i,j,n)) {
                vis[i][j]=1;
                dfs(i,j,t+1,n);
                vis[i][j]=0;
            }
        }
    }
}
int main() {
    int n;
    cin>>n;
    for(int i=1; i<=n; i++) {
        for(int j=1; j<=n; j++) {
            cin>>map[i][j];
        }
    }
    dfs(1,1,0,n);
    for(int i=1; i<=n*n;i++) {
        if(num[i]!=0) cout<<num[i]<<endl;
    }
    return 0;
}

それがあなたを助けるなら、それは大きな名誉です!

元の記事を20件公開 15 獲得 ビュー217

おすすめ

転載: blog.csdn.net/qq_37414463/article/details/105373646