Javaのバックトラッキング
4607 ワード
バックトラッキングは、一度に一度に解決策を構築しようとすることによって問題を再帰的に解決しようとするアルゴリズムによって再帰的に問題を解決するアルゴリズムであり、時間の任意の時点で問題の制約を満たすことができない解決策を削除する(ここでは、検索ツリーの任意のレベルに達するまでの経過時間を参照).
バックトラッキングには3種類の問題があります.
1)決定問題−これにより実現可能解を探索する.
2)最適化問題−これにより最適解を求めた.
3)列挙問題:これにより実現可能解を見出した.
4 )バックトラッキングを用いて問題が解決できるかどうかを判断する方法は?
一般に、すべての制約充足問題は、任意の目的解決において明確に定義された制約条件を持ち、その結果、候補者が段階的に解決の候補を構築し、候補が(有効性のある解決策に完成することができないと判断するとすぐに)、候補を放棄する.しかし、議論される問題の大部分は、入力サイズの順序で対数、線形、線形対数時間複雑さでダイナミックプログラミングまたは貪欲なアルゴリズムのような他の既知のアルゴリズムを使用して解決されることができて、したがって、あらゆる点(バックトラッキングアルゴリズムが一般的に時間とスペースで指数関数的であるので)でバックトラッキングアルゴリズムをアウトシャインします.しかし、まだいくつかの問題が残っています.
あなたの目の前に3つのボックスを持っている状況を考慮し、それらの1つだけで金のコインを持っているが、あなたはどちらを知らない.だから、コインを得るためには、1つずつボックスのすべてを開く必要があります.あなたは最初の箱をチェックします、それがコインを含まないならば、あなたはコインを見つけるまで、それを閉じて、第2の箱をチェックしなければなりません.これは、バックトラッキングは、すべてのサブ問題を1つずつ解決するために最善の解決策に到達することです.
以下の例を考えて、より後方にアプローチを理解する.
インスタンスに対応する計算上の問題PとデータDのインスタンスを考えると、この問題を解決するために満足すべき制約は全てCで表される.バックトラッキングアルゴリズムは次のように動作します.
アルゴリズムは、解決策を構築し始める.S ={ }
sに残っている最初の動きに追加(すべての可能な動きは1つずつsに追加されます).これは今、アルゴリズムの検索ツリーに新しいサブツリーsを作成します.
s + sがCの制約の各々を満たすかどうかチェックしてください.
yesの場合、サブツリーのSは“より多くの”子供を追加する“適格”です.
他の部分木sは役に立たないので、引数sを使ってステップ1に戻る.
新しく作成されたサブツリーsの「適格性」の場合、引数s + sを使ってステップ1に戻ります.
S + SのチェックがデータD全体の解決策であることを返します.プログラムを出力し終了します.
そうでなければ、現在のsで解決が可能でないので、それを捨ててください.
再帰とバックトラッキングの違い
再帰では、関数は基底ケースに到達するまで呼び出します.バックトラッキングでは、我々は問題のための最良の結果を得るまですべての可能性を探るために再帰を使用します.
バックトラッキングのための擬似コード再帰的バックトラッキングソリューション. void findsolution ( n , other params ):
(
この関数は、
ディスプレーメソッド();
を返します.
システム終了( 0 );
リターン
は解決が存在するかどうか boolean findsolution ( n , other params ):
(
ディスプレーメソッド();
返り値TRUE ;
n女王は、2つの女王が互いを攻撃するように、n×nチェス盤にnチェス女王を置く問題です.たとえば、次の4女王の問題の解決策です.
予想される出力は、クイーンが置かれるブロックの1 sを持つバイナリ行列です.例えば、上記4つのクイーン溶液に対する出力行列が次のようになる.
{ 0 , 1 , 0 , 0 }
{ 0 , 0 , 0 , 1 }
{ 0 , 0 , 0 }
{ 0 , 0 , 1 , 0 }
バックトラッキングアルゴリズム:アイデアは、左端の列から始まる、別の列で1つずつクイーンズ1を配置することです.我々が列に女王を置くとき、我々はすでに置かれた女王と衝突をチェックします.現在の列では、衝突がない行を見つけた場合、この行と列をソリューションの一部としてマークします.衝突によってそのような行が見つからない場合は、バックトラックとfalseを返します.
1 )左端のカラムから始める
2 )すべての女王が置かれるなら
返り値
3 )現在の列のすべての行を試してください.すべての試み行に従ってください.
女王がこの列に無事に置かれたら、この行をマークしてください.
ソリューションの一部として、
ここの女王は、解決に至ります.
女王さまが列を列に並べると解決します
本当です.
クイーンを置くと解決策にならない場合は、このマークを外してステップ( a )に進んで行を試してください.
4 )全ての行が試行され、何も動作しない場合、falseを返します.
バックトラッキング.
**バックトラッキングの例
プログラム- 1 :
Javaをインポートします.Utilスキャナ
パブリッククラスpro 1 {
public static void main ( string [] args ) {
スキャナスキャナ=新しいスキャナ
システムアウト.println ( mとnの値を入力します);
int m =スキャナ.next ()
int =スキャナ.next ()
int result =グリッド( 1 , 1 , m , n );
システムアウト.println ("結果:"結果);
}
パブリック静的intグリッド( int x , int y , int m , int n )
を返します.
1を返す
}
他の場合( x <= m & y <= n ){ { }
リターングリッド( x + 1 , y , m , n )+グリッド( x , y + 1 , m , n );
}
その他
0を返す
}
}
}
プログラム2 :
Javaをインポートします.Utilスキャナ
Javaをインポートします.数学
パブリッククラス
プライベート静的ダブルマックス=数学.pow ( 10 , 9 ) + 7 ;
パブリックstatic int countorders ( int n ) {
int sumtinnow = 1 ;
ダブルプロダクション
( int i = 2 ; i <= n ; i++) {
( int j = 2 * i - 2 ; j <= 2 * i - 1 - j++)
Sumtillnow += J ;
}
これは、
以下に示すようになります.
}
ProductNowを返します.
}
public static void main ( string [] args ) {
スキャナスキャナ=新しいスキャナ
システムアウト.println (
int =スキャナ.next ()
システムアウト.println ("結果:+ countorders ( n ));
}
}
プログラム- 3
Javaをインポートします.Utilスキャナ
パブリッククラスpro 3 {
プライベートstatic int mod = 1000000007 ;
public static void main ( string [] args ) {
スキャナスキャナ=新しいスキャナ
システムアウト.println ("Enter NとK :");
int =スキャナ.next ()
スキャナー.next ()
システムアウト.println ("結果:"+再配置( n , k ));
}
パブリック静的int再配列( int n , int k ) {
int [][] dp = new int [ n + 1 ][ k + 1 ];
returnヘルパー( n , k , dp );
}
パブリック静的intヘルパー( int n , int k , int [][] dp ) {
int result = 0 ;
( n == k )
1を返す
}
を返します.
0を返す
}
を返します.
DP [ n ][ k ]= ( int ) ( 1 l *ヘルパー( n - 1 , k - 1 , dp )+ 1 l *ヘルパー( n - 1 , k , dp )*( n - 1 )% % mod );
}
戻り値
}
}
注意:
より多くの例については、ちょうどLeetCodeと実践..
バックトラッキングには3種類の問題があります.
1)決定問題−これにより実現可能解を探索する.
2)最適化問題−これにより最適解を求めた.
3)列挙問題:これにより実現可能解を見出した.
4 )バックトラッキングを用いて問題が解決できるかどうかを判断する方法は?
一般に、すべての制約充足問題は、任意の目的解決において明確に定義された制約条件を持ち、その結果、候補者が段階的に解決の候補を構築し、候補が(有効性のある解決策に完成することができないと判断するとすぐに)、候補を放棄する.しかし、議論される問題の大部分は、入力サイズの順序で対数、線形、線形対数時間複雑さでダイナミックプログラミングまたは貪欲なアルゴリズムのような他の既知のアルゴリズムを使用して解決されることができて、したがって、あらゆる点(バックトラッキングアルゴリズムが一般的に時間とスペースで指数関数的であるので)でバックトラッキングアルゴリズムをアウトシャインします.しかし、まだいくつかの問題が残っています.
あなたの目の前に3つのボックスを持っている状況を考慮し、それらの1つだけで金のコインを持っているが、あなたはどちらを知らない.だから、コインを得るためには、1つずつボックスのすべてを開く必要があります.あなたは最初の箱をチェックします、それがコインを含まないならば、あなたはコインを見つけるまで、それを閉じて、第2の箱をチェックしなければなりません.これは、バックトラッキングは、すべてのサブ問題を1つずつ解決するために最善の解決策に到達することです.
以下の例を考えて、より後方にアプローチを理解する.
インスタンスに対応する計算上の問題PとデータDのインスタンスを考えると、この問題を解決するために満足すべき制約は全てCで表される.バックトラッキングアルゴリズムは次のように動作します.
アルゴリズムは、解決策を構築し始める.S ={ }
sに残っている最初の動きに追加(すべての可能な動きは1つずつsに追加されます).これは今、アルゴリズムの検索ツリーに新しいサブツリーsを作成します.
s + sがCの制約の各々を満たすかどうかチェックしてください.
yesの場合、サブツリーのSは“より多くの”子供を追加する“適格”です.
他の部分木sは役に立たないので、引数sを使ってステップ1に戻る.
新しく作成されたサブツリーsの「適格性」の場合、引数s + sを使ってステップ1に戻ります.
S + SのチェックがデータD全体の解決策であることを返します.プログラムを出力し終了します.
そうでなければ、現在のsで解決が可能でないので、それを捨ててください.
再帰とバックトラッキングの違い
再帰では、関数は基底ケースに到達するまで呼び出します.バックトラッキングでは、我々は問題のための最良の結果を得るまですべての可能性を探るために再帰を使用します.
バックトラッキングのための擬似コード
(
この関数は、
ディスプレーメソッド();
を返します.
システム終了( 0 );
リターン
for (val = first to last) :
if (isValid(val, n)) :
applyValue(val, n);
findSolutions(n+1, other params);
removeValue(val, n);
(
ディスプレーメソッド();
返り値TRUE ;
for (val = first to last) :
if (isValid(val, n)) :
applyValue(val, n);
if (findSolutions(n+1, other params))
return true;
removeValue(val, n);
return false;
標準的なバックトラッキング問題、n -クイーン問題を解決しようとしましょう.n女王は、2つの女王が互いを攻撃するように、n×nチェス盤にnチェス女王を置く問題です.たとえば、次の4女王の問題の解決策です.
予想される出力は、クイーンが置かれるブロックの1 sを持つバイナリ行列です.例えば、上記4つのクイーン溶液に対する出力行列が次のようになる.
{ 0 , 1 , 0 , 0 }
{ 0 , 0 , 0 , 1 }
{ 0 , 0 , 0 }
{ 0 , 0 , 1 , 0 }
バックトラッキングアルゴリズム:アイデアは、左端の列から始まる、別の列で1つずつクイーンズ1を配置することです.我々が列に女王を置くとき、我々はすでに置かれた女王と衝突をチェックします.現在の列では、衝突がない行を見つけた場合、この行と列をソリューションの一部としてマークします.衝突によってそのような行が見つからない場合は、バックトラックとfalseを返します.
1 )左端のカラムから始める
2 )すべての女王が置かれるなら
返り値
3 )現在の列のすべての行を試してください.すべての試み行に従ってください.
女王がこの列に無事に置かれたら、この行をマークしてください.
ソリューションの一部として、
ここの女王は、解決に至ります.
女王さまが列を列に並べると解決します
本当です.
クイーンを置くと解決策にならない場合は、このマークを外してステップ( a )に進んで行を試してください.
4 )全ての行が試行され、何も動作しない場合、falseを返します.
バックトラッキング.
**バックトラッキングの例
プログラム- 1 :
Javaをインポートします.Utilスキャナ
パブリッククラスpro 1 {
public static void main ( string [] args ) {
スキャナスキャナ=新しいスキャナ
システムアウト.println ( mとnの値を入力します);
int m =スキャナ.next ()
int =スキャナ.next ()
int result =グリッド( 1 , 1 , m , n );
システムアウト.println ("結果:"結果);
}
パブリック静的intグリッド( int x , int y , int m , int n )
を返します.
1を返す
}
他の場合( x <= m & y <= n ){ { }
リターングリッド( x + 1 , y , m , n )+グリッド( x , y + 1 , m , n );
}
その他
0を返す
}
}
}
プログラム2 :
Javaをインポートします.Utilスキャナ
Javaをインポートします.数学
パブリッククラス
プライベート静的ダブルマックス=数学.pow ( 10 , 9 ) + 7 ;
パブリックstatic int countorders ( int n ) {
int sumtinnow = 1 ;
ダブルプロダクション
( int i = 2 ; i <= n ; i++) {
( int j = 2 * i - 2 ; j <= 2 * i - 1 - j++)
Sumtillnow += J ;
}
これは、
以下に示すようになります.
}
ProductNowを返します.
}
public static void main ( string [] args ) {
スキャナスキャナ=新しいスキャナ
システムアウト.println (
int =スキャナ.next ()
システムアウト.println ("結果:+ countorders ( n ));
}
}
プログラム- 3
Javaをインポートします.Utilスキャナ
パブリッククラスpro 3 {
プライベートstatic int mod = 1000000007 ;
public static void main ( string [] args ) {
スキャナスキャナ=新しいスキャナ
システムアウト.println ("Enter NとK :");
int =スキャナ.next ()
スキャナー.next ()
システムアウト.println ("結果:"+再配置( n , k ));
}
パブリック静的int再配列( int n , int k ) {
int [][] dp = new int [ n + 1 ][ k + 1 ];
returnヘルパー( n , k , dp );
}
パブリック静的intヘルパー( int n , int k , int [][] dp ) {
int result = 0 ;
( n == k )
1を返す
}
を返します.
0を返す
}
を返します.
DP [ n ][ k ]= ( int ) ( 1 l *ヘルパー( n - 1 , k - 1 , dp )+ 1 l *ヘルパー( n - 1 , k , dp )*( n - 1 )% % mod );
}
戻り値
}
}
注意:
より多くの例については、ちょうどLeetCodeと実践..
Reference
この問題について(Javaのバックトラッキング), 我々は、より多くの情報をここで見つけました https://dev.to/nikhilesh2601/backtracking-in-java-3la0テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol