[NOIP2004普及グループ] FBIツリーキューソリューション

[NOIP2004普及グループ] FBIツリー

タイトル説明:

0 と 1 で構成される文字列は 3 つのカテゴリに分類できます。すべて 0 の文字列は B 文字列と呼ばれ、すべての 1 の文字列は I 文字列と呼ばれ、0 と 1 の両方を含む文字列は F 文字列と呼ばれます。

FBIツリーはバイナリツリーであり、ノードタイプもFノード、Bノード、Iノードの3種類があります。FBI ツリー T は、長さが $2^N$ の 01 文字列 S から構築できます。再帰的な構築方法は次のとおりです。

1. T のルート ノードは R であり、その型は文字列 S と同じです;
2. 文字列 S の長さが 1 より大きい場合、文字列 S を中央から分離し、左右の部分文字列 S1 に分割します。部分文字列 S1 は R の左側の部分木 T1 を構築し、R の右側の部分木 T2 は右側の部分文字列 S2 から構築されます。

長さ 2^N の 01 文字列が与えられた場合、上記の構築方法を使用して FBI ツリーを構築し、そのポストオーダー トラバーサル シーケンスを出力してください。

入力フォーマット

最初の行は整数 N(0≤N≤10) です。

2 行目は、長さ 2^N の 01 文字列です。

出力フォーマット

文字列、FBI ツリーの事後探索シーケンス。

入力サンプルと出力サンプル

入力 #1:

3 
10001011

出力 #1:

IBFBBBBFIBFIIIFF

指示/ヒント

データの %40% では、N≤2。

すべてのデータについて N≤10。

noip2004普及グループの質問3。

トピックの主なアイデア:

このトピックの一般的な考え方は、 10001011   などのシーケンスが与えられた場合、シーケンスを中央から分離し、左側のサブツリーが左側のサブツリー、右側のサブツリーが右側のサブツリーであるということです。すべて0とすべて1またはすべて10、値F、B、Iがあり、バイナリツリーを構築し、それを逆順に出力します

再帰的な解決策:

  このソリューションはキュー ソリューションよりもはるかに簡単です。

[NOIP2004 普及グループ] FBI ツリー再帰ソリューションicon-default.png?t=N4P3http://t.csdn.cn/YK8MW

キューソリューション:

二分木のポストオーダー出力を取得するにはどうすればよいですか?

このバイナリ ツリーの階層トラバーサルを取得して配列に配置するだけで済みます。ポストオーダー トラバーサル コードは次のとおりです
(一般的な考え方は、最初に左のサブツリーが存在するかどうかを判断し、存在する場合はそれをトラバースし、次に、右側のサブツリーをトラバースし、最後にルート ノードを出力します)

void print(ll h) {
    ll l=h*2,r=h*2+1;
    if(l<=size) 
	  print(l);
    if(r<=size) 
	  print(r);
    cout<<S[h];
}

ここで必要なのは、このバイナリ ツリーのレベル トラバーサル配列を取得することだけです。

先頭のシーケンスについては、中央から分割します。つまり、1000 と 1011 の 2 つの部分に分割します。最初、このシーケンスには 0 と 1 の両方があるため、ルート ノードは F で、F の左側のサブツリーは次のようになります。 1000 (これは F)、右側のサブツリー 1011 も F であり、これら 3 つの値を階層トラバーサル配列に順番に格納します。

次に、左側のサブツリーの値を 2 つの部分に分けて走査する必要があります。階層的な走査の場合、単純に再帰を使用して解決することはできないことに注意してください。再帰を使用する場合は、最も深いリーフ ノードまで再帰してから開始します。右へ。

以下の図に示すように、10 00 10 11 の値を階層型走査配列に順次入力する必要がありますが、再帰を使用して 2 つの範囲を解決する場合は、下図のコードと同様に、最初に左側の部分を解決してから右側の部分を解決します。最初に10と00に分けて、配列に10を入れて1と0に分けて出てきて右側に00を入れて、次に入って0を2つ格納します。

ただし、私たちの要件は、1000 を 2 つの値に分割して配列に格納し、その後 1000 のサブツリーをトラバースし続けるのではなく、1011 を直接トラバースすることです。サブツリー部分を次の範囲に配置して解決する必要があります。

では、この問題をどうやって解決するのでしょうか?
1000 と 1011 に分割した後、これら 2 つの範囲をさらに小さい範囲に分割して、階層走査配列に格納する必要があるのではないでしょうか? 1000 を 10 と 00 に分割した後、これら 2 つの範囲は一時的に処理せず、後の処理のためにデータ構造に保存します。
それでは、どのデータ構造がデータの保存に適しているのでしょうか? ストレージの特性について考えてみましょう。最初に保存されたものは最初に処理され、後で保存されたものは後で処理されます。これはデータ構造 (キュー) にぴったり当てはまります。1000 と 1011 については、最初に 1000 を処理し、10 と 00 の 2 つの部分に分割します。この 2 つの範囲は一時的に処理せず、キューに格納します。その後、1011 を処理します。10 と 11 に分割した後、これもキューに格納され、この時のキューの構造はこんな感じです

これら 4 つの対応する文字 10(F) 00(B) 10(F) 11(I) を階層走査配列に格納した後、チームの先頭から 10 を取得し、さらに処理を実行し、処理後の値を継続します。キューに入れることは、次の層で処理する範囲を示します。

このようにして、FBI ツリーが完全に構築されたことを示す要素がキュー内になくなるまで、対応する階層トラバーサル配列も取得され、上記のコードを使用してツリーの事後順序を出力できます

解析コード

キューについては、stl ライブラリのキューを使用できます。ここでは配列を使用してキューをシミュレートします。もちろん、手動でキューを作成することもできます。キューには 1 から 2^N までの初期範囲を格納します
これは処理する最初の要素でもあります

head は現在のキューの先頭を指し、tail はキューの末尾であり、現在処理される最後のデータです。キューは0 からカウントを開始し、tail の位置にはデータがありませ範囲の場合、左側の境界と境界があるので、構造体を定義し、毎回この構造体をキューに格納します

struct Node{
    ll l,r;
}Q[10020];

    ll h=-1,d=1,p,q;
    Q[++h].l=l;
    Q[h].r=r;

現在のキューに要素がある限り、それらを処理する必要があるため、ループの終了条件は head==tail (キューの先頭がキューの空の端に到達したことを示します) になります。

head を使用して現在のキューの先頭をフェッチするたびに、範囲がすべて 1 かすべて 0 かを判断するために 0 と 1 が使用されます (中間に 1 がある場合はすべて 0 ゼロが false、中間に 1 がある場合はすべて 0 ゼロが false になります)。中央に 0 があれば、すべて 1 になります。一方は false で、最後に両方とも false の場合は、両方とも F、I、B などであることを意味します)

この範囲を処理した後、このデータをキューに入れ、階層トラバーサル配列 (cnt は現在のトラバーサル配列の数を表し、最初は 0) を挿入します。これは head++ です。この範囲の下にサブ範囲がある場合は、 sub-range 2 つの部分に分割し、キューの最後に挿入し続けます。

    bool f1,f2;
    while(h<d){
        p=Q[h].l;
        q=Q[h].r;
        f1=f2=true;
        for(ll i=p;i<=q;i++){
            if(T[i]=='1') 
			  f1=false;
            else if(T[i]=='0') 
			  f2=false;
        }
        if(!f1&&!f2)
          S[++size]='F';
        else if(f2&&!f1)
          S[++size]='I';
        else if(f1&&!f2)
          S[++size]='B';
        h++;
        if(p<q){
            Q[d].l=p;
            Q[d++].r=(p+q)/2;
            Q[d].l=(p+q)/2+1;
            Q[d++].r=q;
        }
    }

階層走査配列を取得した後、シーケンスを直接走査できます。

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

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,size;
char S[10020];
char T[1050];
struct Node{
    ll l,r;
}Q[10020];
inline ll read(){
    ll x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-')
          f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return x*f;
}
void print(ll h) {
    ll l=h*2,r=h*2+1;
    if(l<=size) 
	  print(l);
    if(r<=size) 
	  print(r);
    cout<<S[h];
}
void FBI(ll l,ll r){
    ll h=-1,d=1,p,q;
    Q[++h].l=l;
    Q[h].r=r;
    bool f1,f2;
    while(h<d){
        p=Q[h].l;
        q=Q[h].r;
        f1=f2=true;
        for(ll i=p;i<=q;i++){
            if(T[i]=='1') 
			  f1=false;
            else if(T[i]=='0') 
			  f2=false;
        }
        if(!f1&&!f2)
          S[++size]='F';
        else if(f2&&!f1)
          S[++size]='I';
        else if(f1&&!f2)
          S[++size]='B';
        h++;
        if(p<q){
            Q[d].l=p;
            Q[d++].r=(p+q)/2;
            Q[d].l=(p+q)/2+1;
            Q[d++].r=q;
        }
    }
}
int main(){
    n=read();
    for(ll i=1;i<=pow(2,n);i++)
      cin>>T[i];
    FBI(1,pow(2,n));
    print(1);
    return 0;
}

 要約:

  この問題は比較的単純で、直接再帰的に解決する方法もあります。

トピックリンク:
[NOIP2004 普及グループ] FBI ツリー - 羅谷https://www.luogu.com.cn/problem/P1087

おすすめ

転載: blog.csdn.net/wo_ai_luo_/article/details/130933874