アルゴリズムノート第8章-検索

深さ優先探索

深さ優先探索は、すべての状況をトラバースするためのすべての完全なパスを列挙する検索方法です。

ナップサック問題

ここに画像の説明を挿入
各アイテムには、選択するかどうかを選択する2つのオプションがあります。これは「交差点」に相当し、バックパックの総重量がVより大きい場合は、行き止まりに入ったため、に戻る必要があります。以前の「岐路」。
ここに画像の説明を挿入

#include<cstdio>
const int maxn = 30;
int n,V,maxValue = 0;  //物品件数n,背包容量V,最大价值maxValue
int w[maxn], c[maxn];  //w[i]每个物品重量,c[i]每个物品的价值

//DFS, index为当前处理的物品的编号
//sumW和sumC分别为当前总重量和当前总价值
void DFS(int index,int sumW, int sumC)
{
    
    
    if(index==n){
    
    //已经完成对n件物品的选择
        if(sumW <= V && sumC > maxValue)
        {
    
    
            maxValue = sumC;
        }
        return; 
    }
    DFS(index+1,sumW,sumC); //不选这个物品
    DFS(index+1,sumW+w[index],sumC+c[index]);  //选这个物品
}

int main(){
    
    
    scanf("%d%d",&n,&V);
    for(int i=0;i<n;i++)
    {
    
    
        scanf("%d",&w[i]);
    }
    for(int i=0;i<n;i++)
    {
    
    
        scanf("%d",&c[i]);
    }
    DFS(0,0,0);
    printf("%d\n",maxValue);
    return 0;
}

上記のコードの時間計算量はO(2 n)であり、上記のコードはn個のアイテムすべてが選択されたかどうかにかかわらず、常にmaxValueを更新しますが、選択されたアイテムが選択された後に処理されていないアイテムがある場合があります。選択した重みがVを超えているため、コードを改善できます。
インデックスは、処理されたノードの数を表します。最初は、0はノードが処理されていないことを示します。

#include<cstdio>
const int maxn = 30;
int n,V,maxValue = 0;  //物品件数n,背包容量V,最大价值maxValue
int w[maxn], c[maxn];  //w[i]每个物品重量,c[i]每个物品的价值

//DFS, index为当前处理的物品的编号
//sumW和sumC分别为当前总重量和当前总价值
void DFS(int index,int sumW, int sumC)
{
    
    
    if(index==n) return;
    DFS(index+1,sumW,sumC);  //不选择index+1的后的总重量和总价值
    if(sumW+w[index+1]<=V)
    {
    
    
        if(sumC+c[index+1]>maxValue) maxValue = sumC+c[index+1];
        DFS(index+1,sumW+w[index+1],sumC+c[index+1]);
    }
}

対象条件の制限により計算量を減らす上記の方法は、剪定と呼ばれます。

結果を選択した後、結果を得たソリューションを保存する方法は?

#include<cstdio>
#include<cstdlib>
#include<vector>
using namespace std;
//用一个临时的数组先放着,当价值确实更大的时候,更新给结果数组
const int maxn = 30;
int n,V,maxValue = 0;  //物品件数n,背包容量V,最大价值maxValue
int w[maxn], c[maxn];  //w[i]每个物品重量,c[i]每个物品的价值
vector<int> temp,result;  //临时数组与结果数组

//DFS, index为当前处理的物品的编号
//sumW和sumC分别为当前总重量和当前总价值
void DFS(int index,int sumW, int sumC)
{
    
    
    if(index==n) return;
    DFS(index+1,sumW,sumC); //没选index+1号这个节点
    if(sumW+w[index+1]<=V)  //选择了这个节点
    {
    
    
        temp.push_back(index+1);
        if(sumC+c[index+1]>maxValue){
    
    //如果是更好的方案,那么就更新
            maxValue = sumC+c[index+1];
            result = temp;
        } 
        DFS(index+1,sumW+w[index+1],sumC+c[index+1]);//在这条分支走结束之前是不会,
        //进行下一条语句temp.pop_back的也就是这条分支走完会回溯到最新的节点没有被选择的情况下
        temp.pop_back(); //为了不影响没有选择这个节点的那个分支,记得pop出来,因为temp是个全局变量
    }
}

int main()
{
    
    
	scanf("%d%d",&n,&V);
	for(int i=0;i<n;i++){
    
    
		scanf("%d",&w[i]);
	}
	for(int i=0;i<n;i++){
    
    
		scanf("%d",&c[i]);
	}
	DFS(0,0,0);
	printf("%d\n",maxValue);
	for(int i=0;i<result.size();i++){
    
    
		printf("%d ",result[i]);
	}
	return 0;
}

シーケンス列挙の問題

列挙は、N個の整数からいくつかの数値を選択してそれらの合計がXになるようにし、2乗の合計を最大化しようとします

#include<cstdio>
#include<cstdlib>
#include<vector>
using namespace std;

const int maxn = 100;
int n,k,x,maxSumSqu=-1,A[maxn];
vector<int> temp,ans;
//当前处理index号整数,当前已选整数个数为nowK
//当前已选整数之和为sum,当前已选整数平方和为sumSqu
void DFS(int index,int nowK,int sum,int sumSqu)
{
    
    
    if(nowK==k && sum==x)
    {
    
    
        if(sumSqu>maxSumSqu)
        {
    
    
            maxSumSqu = sumSqu;
            ans = temp;
        }
        return;
    }
    if(index==n || nowK > k || sum >x) return;
    //选index号数
    temp.push_back(A[index]);
    DFS(index+1,nowK + 1, sum+A[index],sumSqu+A[index]*A[index]);
    temp.pop_back();
    //不选index号数
    DFS(index+1,nowK,sum,sumSqu);
}

幅優先探索

マトリックス内の「ブロック」の数

ここに画像の説明を挿入
(上の写真の右上隅に1が1つしかない部分もブロックと見なされるため、合計4つのブロックがあります)

基本的な考え方:各位置の要素を列挙します。0の場合はスキップします。1の場合は、隣接する要素を検索して、すべて1であるかどうかを確認します。
戻るのを防ぐために、通常、ブール型を設定できます。この位置がBFSに参加しているかどうかを判断するための配列in_queue

ここに画像の説明を挿入

#include<cstdio>
#include<queue>
using namespace std;
const int maxn = 100;
struct node{
    
    
    int x,y;
}Node;

int n,m;
int matrix[maxn][maxn];
bool in_queue[maxn][maxn] = {
    
    false};
int X[4] = {
    
    0,0,1,-1};
int Y[4] = {
    
    1,-1,0,0};

//判断(x,y)是否越界或者是否已经被访问过啦
bool judge(int x,int y)
{
    
    
    if(x>=n || x<0 || y>=m || y<0) return false;
    if(matrix[x][y]==0 || in_queue[x][y] == true) return false; //当前位置为0,或者(x,y)已经入队
    return true;
}

void BFS(int x,int y)
{
    
    
    queue<node> Q;
    Node.x = x,Node.y = y;
    Q.push(Node);
    in_queue[x][y] = true;
    while(!Q.empty())
    {
    
    
        node top = Q.front();
        Q.pop();
        for(int i=0;i<4;i++)
        {
    
    
            int newX = top.x + X[i];
            int newY = top.y + Y[i];
            if(judge(newX,newY))
            {
    
    
                Node.x = newX, Node.y = newY;
                Q.push(Node);
                in_queue[newX][newY] = true;
            }
        }
    }
}

int main()
{
    
    
    scanf("%d%d",&n,&m);
    for(int x=0;x<n;x++)
    {
    
    
        for(int y=0;y<m;y++)
        {
    
    
            scanf("%d",&matrix[x][y]);
        }
    }
    int ans = 0 ;//存放块数
    for(int x=0;x<n;x++)
    {
    
    
        for(int y=0;y<m;y++)
        {
    
    
            //如果元素为1并且还没有入队
            if(matrix[x][y]==1 && in_queue[x][y]==false)
            {
    
    
                ans++;
                BFS(x,y);
            }
        }
    }
    printf("%d\n",ans);
    return 0;
}

迷路の最小ステップ数を見つける

ここに画像の説明を挿入
ここに画像の説明を挿入
BFSはレベルの順序でトラバースされるため、トラバースされたレベルの数は開始点Sから記録でき、終了点Tに到達したときのレベル数は、解決する必要があるSからTまでの最小ステップ数です。 。
分岐点のノードは、同じレイヤーであると見なすことができます

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;

const int maxn = 100;
struct node{
    
    
    int x,y;
    int step; //从S到该位置的最小步数,也就是层数
}S,T,Node;  //起点,终点,临时结点

int n,m;
char maze[maxn][maxn];
bool inq[maxn][maxn] = {
    
    false};
int X[4] = {
    
    0,0,1,-1};
int Y[4] = {
    
    1,-1,0,0};

bool test(int x,int y)
{
    
    
    if(x<0 || x>=n || y<0 || y>=m) return false;
    if(maze[x][y]=='*') return false;
    if(inq[x][y]==true) return false;
    return true;
}

int BFS()
{
    
    
    queue<node> q;
    q.push(S);
    while(!q.empty())
    {
    
    
        node top = q.front();
        q.pop();
        if(top.x==T.x && top.y==T.y) return top.step;
        for(int i=0;i<4;i++)
        {
    
    
            int newX = top.x+X[i];
            int newY = top.y+Y[i];
            if(test(newX,newY))
            {
    
    
                Node.x = newX,Node.y = newY;
                Node.step = top.step + 1;
                q.push(Node);
                inq[Node.x][Node.y]=true;
            }
        }
    }
    return -1;//无法到达终点
}

int main()
{
    
    
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++)
    {
    
    
        getchar();
        for(int j=0;j<m;j++)
        {
    
    
            maze[i][j] = getchar();
        }
        maze[i][m+1] = '\0';
    }
    scanf("%d%d%d%d",&S.x,&S.y,&T.x,&T.y);
    S.step = 0;
    printf("%d\n",BFS());
    return 0;
}

ここに画像の説明を挿入
STLキューを使用して要素をエンキューする場合、要素のコピーを作成してエンキューするだけであることに注意してください。キュー内の要素の値が変更されても、元の要素の値は変更されません。

おすすめ

転載: blog.csdn.net/weixin_44972129/article/details/110310545