検索の概要

25896 ワード

                                                 

検索概要検索アルゴリズムは、コンピュータの高性能を利用して、問題の一部またはすべての可能な状況を目的として窮屈に挙げ、問題の解を求める方法である.単純な列挙アルゴリズムに比べて一定の方向性と目標性がある.アルゴリズムは,解の空間において,一つの状態から(要求に応じて広がる)別の状態に移行し,このようにして進み,解の空間における状態を遍歴し,答え(目標の状態)を見つける.検索分類は検索という部分で主に入門するものがあります.例えば、二分、三分、BFS、DFS、その他の比較的総合的なアルゴリズムがあります.例えば、優先キュー+BFS、優先キューBFS+剪定、双方向BFS、DFS+剪定、DFS+パリティ剪定、BFS+剪定、BFS+判断テクニック、BFS+同余剪定など、とにかく複雑で種類が多すぎます.肝心なのは基礎を身につけてから異なるテーマの中で開拓することです.***1.二分探索の基本思想:単調で秩序ある集合の中で要素を探し、毎回集合を左右の2つの部分に分け、どの部分にあるかを判断し、集合の上下境界を調整し、目標要素が見つかるまで繰り返す.
2.三分探索の基本思想:二分法と同様に、ある凸性または凹形関数の極値を要求する必要があり、関数自体の表現式では容易に解くことができない場合、三分法で解に近づくことができる.注:単纯な2分、3分の検索のテーマは比较的に简単で、相応のテーマは検索のテーマの中の前のいくつかの道で、ここで回顧をしないで、相応のブログを见ることができます.
3.BFS基本思想:初期状態Sから,ルールを利用してすべての可能な状態を生成する.構成された次の層ノードは,ターゲット状態Gが出現しているかどうかをチェックし,出現していない場合は,その層のすべての状態ノードに対して,それぞれ規則を順次利用する.さらに次の層のすべての状態ノードを生成し,この層のすべての状態ノードに対してGが出現しているかどうかをチェックし,出現していない場合は,以上のようにして次の層のすべての状態ノードを生成し続け,このように1層ずつ展開する.ターゲットステータスが表示されるまで.
例題:1)Prime Path題意:2つの素数(4桁)を与えて、1番目の数が何回変換して2番目の素数を得ることができることを求めます.変換方式:変換数のいずれかの桁の数字(1位はゼロにできないが、他の変換数字は0~~9)であり、変換後の数も素数である.構想:bfs、検索は最短の経路を求めて、簡単に広さを優先して検索します;広さ優先探索のため,初めて探索したステップ数が最小のステップ数である.判断時の速度を高めるために;素数表を1つ打った.コード:
#include<iostream>
#include<cmath>
#include<queue>
using namespace std;
const int Max = 10005;

void funprime(){
    for(int num = 1000; num < 10000; num ++){
        prime[num] = true;
        for(int i = 2; i <= sqrt(num); i ++)
            if(num % i == 0){
                prime[num] = false;
                break;
            }
        }
}       //         。

int izero(int num, int n){
    int m[5], i;     //     m[5]     ,       ,      。
    for(i = 1; i <= 4; i ++){
        m[i] = num % 10;
        num = num / 10;
    }
    m[n] = 0;
    for(i = 1; i <= 4; i ++)
        num += m[i] * pow(10, i - 1);
    return num;
}       //  num      n  0, num=8792,n=3,   8092。

int main(){
    int n, sta, end;
    bool prime[Max], visit[Max];
    funprime();
    cin >> n;
    while(n --){
        cin >> sta >> end;
        if(sta == end){
            cout << '0' << endl;
            continue;
        }

        bool fine = false;
        int steps = 0;
        queue<int> pri;
        pri.push(sta);
        memset(visit, false, sizeof(visit)); 
        visit[sta] = true;

        while(!pri.empty() && !fine){
            int t = pri.size();
            steps ++;
            while(t --){
                int num = pri.front();
                pri.pop();
                for(int i = 1; i <= 4; i ++){
                    int n = izero(num, i);
                    for(int j = 0; j <= 9; j ++){
                        int now = n + j * pow(10, i - 1);
                        if(now == end){
                            fine = true;  //      fine == true,    。
                            break;
                        }
                        else if(prime[now] && !visit[now]){
                            pri.push(now);
                            visit[now] = true;
                        }
                    }
                }
            }
        }
        if(fine) cout << steps << endl;
        else cout << "Impossible" << endl;
    }
    return 0;
}

2)Pots題意:2つの容積がそれぞれaとbのpotを与え,以下の3つの操作方式により,一定ステップ数後に使者2つのpotのうちの1つの水量がcであるか否かを求める.1.FILL(i):ipotを水で満たします.2.DROP(i):ipotを空にします.3.POUR(i,j):ipotの水をjpotに注いで、ipotが空になるか、jpotが満タンになるまで.構想:bfsは最短経路を求め,各ノードのサブノード数が6個であるだけである.コード:
#include<iostream>
using namespace std;
const int Max = 101;

struct node{
    int ope; int a; int b; node *pre;
}que[Max * Max]; //     ,ope       ,a,b       pot   。

bool vis[Max][Max];
char str[6][10] = {"FILL(1)", "FILL(2)", "DROP(1)", "DROP(2)", "POUR(1,2)", "POUR(2,1)"};
 //            。

int min(int a, int b){
    return a < b ? a : b;
}

void print(node now){
    if(now.pre != NULL){
        print(*now.pre);
        cout << str[now.ope] << endl;
    }
}

void bfs(int a, int b, int c){
    int steps = 0;
    int head = 0, tail = 1;
    que[0].a = que[0].b = 0;
    que[0].pre = NULL; 
    while(tail - head > 0){
        int count = tail - head;
        while(count --){
            node now = que[head];
            if(now.a == c || now.b == c){
                cout << steps << endl;
                print(now);
                return;
            }
            if(!vis[a][now.b]){
                que[tail].ope = 0;
                que[tail].a = a;
                que[tail].b = now.b;
                que[tail].pre = &que[head];
                vis[a][now.b] = true;
                tail ++;
            }
            if(!vis[now.a][b]){
                que[tail].ope = 1;
                que[tail].a = now.a;
                que[tail].b = b;
                que[tail].pre = &que[head];
                vis[now.a][b] = true;
                tail ++;
            }
            if(!vis[0][now.b]){
                que[tail].ope = 2;
                que[tail].a = 0;
                que[tail].b = now.b;
                que[tail].pre = &que[head];
                vis[0][now.b] = true;
                tail ++;
            }
           if(!vis[now.a][0]){
                que[tail].ope = 3;
                que[tail].a = now.a;
                que[tail].b = 0;
                que[tail].pre = &que[head];
                vis[now.a][0] = true;
                tail ++;
            }
            int wat1 = min(now.a, b - now.b);
            if(!vis[now.a - wat1][now.b + wat1]){
                que[tail].ope = 4;
                que[tail].a = now.a - wat1;
                que[tail].b = now.b + wat1;
                que[tail].pre = &que[head];
                vis[now.a - wat1][now.b + wat1] = true;
                tail ++;
            }
            int wat2 = min(a - now.a, now.b);
            if(!vis[now.a + wat2][now.b - wat2]){
                que[tail].ope = 5;
                que[tail].a = now.a + wat2;
                que[tail].b = now.b - wat2;
                que[tail].pre = &que[head];
                vis[now.a + wat2][now.b - wat2] = true;
                tail ++;
            }
            head ++;
        }
        steps ++;
    }
    cout << "impossible" << endl;
}

int main(){
    int a, b, c;
    cin >> a >> b >> c;
    memset(vis, false, sizeof(vis));
    vis[0][0] = true;
    bfs(a, b, c);
    return 0;
}

4.DFSの基本思想:初期状態から、ルールを利用して検索ツリーの次の層のいずれかのノードを生成し、目標状態が現れていないかどうかを検査し、現れていない場合、この状態でルールを利用して次の層のいずれかのノードを生成し、再検査し、リーフノード(すなわち、新しい状態ノードを生成できない)まで繰り返し、それが目標状態でない場合、前の層の結果に遡る.検索を拡張する可能性のある別のブランチを取ります.同じ方法で目標状態が見つかるまで続けます.
例題:1)Graph Colloring:題名説明:1枚の無方向図を与えて、今図の中で白と黒を着て、隣接する2点の色が異なっていることを要求して、最も多い黒点の個数を求めます.方法:開始点dfsコードを列挙する:
#define N 105
#include<iostream>
#include<vector>
using namespace std;
vector<int>map[N];
int n,m,cnt,tmp;
int ans[N],path[N];
int color[N];

bool ok(int u,int flag)
{
       for(int i=0;i<map[u].size();i++)
       {
              int v=map[u][i];
              if(color[v]==flag)
                     return false;
       }
       return true;
}

void dfs(int u,int flag)
{
       int i;
       for(i=0;i<map[u].size();i++)
       {
              int v=map[u][i];
              if(ok(v,!flag)&&color[v]==-1)
              {             
                     color[v]=!flag;
                     if(!flag==0)
                            path[tmp++]=v;
                     dfs(v,!flag);
              }
       }
}

int main()
{
       int i,j,t,a,b;
       cin>>t;
       while(t--)
       {
              cin>>n>>m;
              for(i=0;i<=n;i++)
                     map[i].clear();
              while(m--)
              {
                     scanf("%d%d",&a,&b);
                     map[a].push_back(b);
                     map[b].push_back(a);
              }
              cnt=0;
              for(i=1;i<=n;i++)
              {
                     memset(color,-1,sizeof(color));
                     path[0]=i;
                     tmp=1;
                     color[i]=0;
                     dfs(i,0);
                     if(tmp>cnt)
                     {
                            for(j=0;j<tmp;j++)
                                   ans[j]=path[j];
                            cnt=tmp;
                     }
              }
              cout<<cnt<<endl;
              cout<<ans[0];
              for(i=1;i<cnt;i++)
                     cout<<" "<<ans[i];
              cout<<endl;
       }
       return 0;
}

2)Flip Gameの題意:4*4の行列で、各格は白か黒か.任意のグリッドを反対の色に変更すると、このグリッドの上、下、左、右の4つのグリッドも反対の色に変更されます(存在する場合).マトリクスのすべての格子を同じ色にするには、少なくとも何回か上の操作を実行する必要があります.構想:列挙+dfs.1つのキー:各グリッドに対して、0または1回しか反転できません(易証).したがって,列挙は2^16=4096の状態であり,最大16回の操作を実行するため可能である.コード:
#include<iostream>
using namespace std;

bool map[6][6], find = false;
int step;
int dr[5] = {-1, 0, 0, 0, 1};
int dc[5] = {0, -1, 0, 1, 0};

bool isgoal(){                           //                  。
    for(int i = 1; i <= 4; i ++)
        for(int j = 1; j <= 4; j ++)
            if(map[i][j] != map[1][1])
                return false;
    return true;
}

void flip(int row, int col){             //    (row,col)  map[][]   。
    for(int i = 0; i < 5; i ++){
        int r = row + dr[i];
        int c = col + dc[i];
        map[r][c] = !map[r][c];
    }
}

void dfs(int row, int col, int dep){     //  (row,col)          。
    if(dep == step){
        find = isgoal();
        return;
    }
    if(find || row == 5) return;

    flip(row, col);                      //    (row,col)    。
    if(col < 4) dfs(row, col + 1, dep + 1);
    else dfs(row + 1, 1, dep + 1);

    flip(row, col);                      //     ,   (row,col)    。
    if(col < 4) dfs(row, col + 1, dep);
    else dfs(row + 1, 1, dep);
}

int main(){
    char c;
    for(int i = 1; i <= 4; i ++)
        for(int j = 1; j <= 4; j ++){
            cin >> c;
            if(c == 'b') map[i][j] = true;
       }
    for(step = 0; step <= 16; step ++){   //   0 ~ 16  。
        dfs(1, 1, 0);
        if(find) break;
    }
    if(find) cout << step << endl;
    else cout << "Impossible" << endl;
    return 0; 
}