ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

算法提高课 第二章 搜索之DFS

2022-08-18 17:00:34  阅读:130  来源: 互联网

标签:第二章 return int DFS st 算法 dfs ans include


一、DFS之连通性模型

1112. 迷宫

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 110;

int T,n;
char g[N][N];
int sx,sy,ex,ey;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
bool st[N][N],f;
void dfs(int x,int y)
{
    if(x == ex && y == ey)
    {
        f = 1;
        return;
    }
    st[x][y] = 1;
    for(int i = 0;i<4;i++)
    {
        int nx = x + dx[i],ny = y + dy[i];
        if(nx<0||nx>=n||ny<0||ny>=n||g[nx][ny] == '#'||st[nx][ny]) continue;
        dfs(nx,ny);
    }
}
int main()
{
    cin>>T;
    while(T--)
    {
        memset(st,0,sizeof st);
        cin>>n;
        for(int i = 0;i<n;i++)
        {
            for(int j = 0;j<n;j++)
            {
                cin>>g[i][j];
            }
        }
        cin>>sx>>sy>>ex>>ey;
        if(g[sx][sy] == '#'||g[ex][ey] == '#')
        {
            puts("NO");
            continue;
        }
        f = false;
        dfs(sx,sy);
        if(f) puts("YES");
        else puts("NO");
    }
    return 0;
}

1113. 红与黑

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 25;

char g[N][N];
int n,m,T;
bool st[N][N];
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int ans;
void dfs(int x,int y)
{
    st[x][y] = 1;
    ++ans;
    for(int i = 0;i<4;i++)
    {
        int nx = x + dx[i],ny = y + dy[i];
        if(nx<1||nx>n||ny<1||ny>m||st[nx][ny]||g[nx][ny] == '#') continue;
        dfs(nx,ny);
    }
    return;
}
int main()
{
    while(cin>>m>>n,n||m)
    {
        ans = 0;
        memset(st, 0, sizeof st);
        int sx,sy;
        for (int i = 1; i <= n; i ++ )
        {
            for(int j = 1;j<=m;j++)
            {
                cin>>g[i][j];
                if(g[i][j] == '@')
                {
                    sx = i,sy = j;  
                }
            }
        }
        dfs(sx,sy);
        cout<<ans<<endl;
    }
    return 0;
}

1118. 分成互质组

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

const int N = 15;

int group[N][N];//第i组第j个元素
int n,a[N];
int ans = 0x3f3f3f3f;
bool st[N];

int gcd(int a,int b)
{
    return b==0?a:gcd(b,a%b);
}

bool check(int g,int gc,int x) //判断x是否与g组所有元素互质
{
    for(int i = 0;i<gc;i++)
    {
        if(gcd(group[g][i],x)>1) return false;
    }
    return true;
}
void dfs(int g,int gc,int u,int start) //g:第几组,gc:组里有多少个数 u:当前搜到了第几个元素 start:搜索起始点
{
    if(g >= ans) return;//当前组数超过了答案,一定不可行,剪枝
    if(u == n) //搜完了所有元素,求出组数最小值
    {
        ans = min(ans,g);
        return;
    }
    bool f = false; //当前组是否添加进新元素
    for(int i = start;i<n;i++)
    {
        if(!st[i] && check(g,gc,a[i]))//没有添加过且与组里元素都互质
        {
            group[g][gc] = a[i];//添加进组
            st[i] = true;
            dfs(g,gc+1,u+1,start+1);//搜索下一个元素
            f = true;//修改已添加
            
            st[i] = false;//恢复现场

        }
    }
    if(!f) //当前组无法添加元素,新开一个组,从头开始搜
    {
        dfs(g+1,0,u,0);
    }
}
int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ )
    {
        scanf("%d", &a[i]);
    }
    dfs(1,0,0,0);
    cout<<ans<<endl;
    return 0;
}

二、DFS之搜索顺序

1116. 马走日

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long LL;

const int N = 10;

int T,n,m,x,y;
LL ans;
int dx[] = {-1,-2,-2,-1,1,2,2,1},dy[] = {-2,-1,1,2,2,1,-1,-2};
bool st[N][N];
void dfs(int x,int y,int cnt)
{
    if(cnt == n*m) //注意:搜到第n*m个点时就可以返回了
    {
        ++ans;
        return;
    }
    st[x][y] = 1;
    for(int i = 0;i<8;i++)
    {
        int nx = x + dx[i],ny = y + dy[i];
        if(nx<0||nx>=n||ny<0||ny>=m||st[nx][ny]) continue;
        dfs(nx,ny,cnt+1);
    }
    st[x][y] = 0;//注意:恢复现场写在外面
}
int main()
{
    cin>>T;
    while(T--)
    {
        ans = 0;
        memset(st,0,sizeof st);
        cin>>n>>m>>x>>y;
        dfs(x,y,1);
        cout<<ans<<endl;
    }
    return 0;
}

1117. 单词接龙

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>

using namespace std;

const int N = 25;

string word[N];
int n;
int ans;
int st[N];

void dfs(string cur,int u) //理解dfs函数形参结构,cur为当前拼接字符串,u为最后一次加入的字符串
{
    //cout<<cur<<endl;
    ans = max(ans,(int)cur.size());
    ++st[u];//不能写进循环体内
    for(int i = 0;i<n;i++) //枚举所有字符串
    {
        if(st[i] == 2) continue; //注意次数限制
        
            for(int j = 1;j<min(cur.size(),word[i].size());j++)  
            {
                if(cur.substr(cur.size()-j) == word[i].substr(0,j)) //能拼接则进行拼接
                {
                     dfs(cur + word[i].substr(j),i);
                }
            }
        
    }
    --st[u];//不能写进循环体内
}
int main()
{
    cin>>n;
    for(int i = 0;i<n;i++)
    {
        cin>>word[i];
    }
    char c;
    cin>>c;
    for(int i = 0;i<n;i++)
    {
        if(word[i][0] == c)
        {
            dfs(word[i],i);
        }
    }
    cout<<ans<<endl;
    return 0;
}

1118. 分成互质组

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

const int N = 15;

int group[N][N];//第i组第j个元素
int n,a[N];
int ans = 0x3f3f3f3f;
bool st[N];

int gcd(int a,int b)
{
    return b==0?a:gcd(b,a%b);
}

bool check(int g,int gc,int x) //判断x是否与g组所有元素互质
{
    for(int i = 0;i<gc;i++)
    {
        if(gcd(group[g][i],x)>1) return false;
    }
    return true;
}
void dfs(int g,int gc,int u,int start) //g:第几组,gc:组里有多少个数 u:当前搜到了第几个元素 start:搜索起始点
{
    if(g >= ans) return;//当前组数超过了答案,一定不可行,剪枝
    if(u == n) //搜完了所有元素,求出组数最小值
    {
        ans = min(ans,g);
        return;
    }
    bool f = false; //当前组是否添加进新元素
    for(int i = start;i<n;i++)
    {
        if(!st[i] && check(g,gc,a[i]))//没有添加过且与组里元素都互质
        {
            group[g][gc] = a[i];//添加进组
            st[i] = true;
            dfs(g,gc+1,u+1,start+1);//搜索下一个元素
            f = true;//修改已添加
            
            st[i] = false;//恢复现场

        }
    }
    if(!f) //当前组无法添加元素,新开一个组,从头开始搜
    {
        dfs(g+1,0,u,0);
    }
}
int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ )
    {
        scanf("%d", &a[i]);
    }
    dfs(1,0,0,0);
    cout<<ans<<endl;
    return 0;
}

三、DFS之剪枝

image

165. 小猫爬山

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 20;

int sum[N],c[N],n,w;
int ans;

void dfs(int cnt,int u) //cnt表示当前用了几辆车,u表示枚举到第几只猫
{
    if(cnt >= ans) return;
    if(u == n+1)
    {
        ans = min(ans,cnt);
        return;
    }
    
    for(int i = 0;i<cnt;i++) //枚举所有车,能放第u只猫则放
    {
        if(sum[i] + c[u] > w) continue; //可行性剪枝
        sum[i] += c[u]; //加入第i辆车
        dfs(cnt,u+1); //为下一只猫找车
        sum[i] -= c[u]; //注意:恢复现场
    }
    
    sum[cnt] = c[u]; //新开一辆车,放第u只猫
    dfs(cnt+1,u+1); //枚举下一只猫
    sum[cnt] = 0; //恢复现场
    return;
}
bool cmp(int a,int b)
{
    return a>b; //必须是>号
}

int main()
{
    cin>>n>>w;
    for(int i = 1;i<=n;i++)
    {
        cin>>c[i];
    }
    ans = n;
    sort(c+1,c+n+1,cmp); //降序排序,减少搜索层数
    dfs(0,1);
    cout<<ans<<endl;
    return 0;
}

数独

image

优化搜索顺序:优先搜索分支较小的,即格子中可能的数较少的
可行性剪枝:所在行、所在列以及所在九宫格中没有重复数字
位运算优化:用九位二进制数表示1~9是否可用,行、列及九宫格三个值进行与运算,lowbit取出所有的1,减少循环次数

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 9,M = 1<<N;

int ones[M];//预处理:一个状态里面有多少个1
int map[M]; //预处理:lowbit返回的是2的次幂,因此需要求出2的几次幂 map(x) = log2(x)
int row[N],col[N],cell[3][3]; //每一行、每一列、每个九宫格能放的数字的二进制表示

char str[100];

int lowbit(int x)
{
    return x & -x;
}
int get(int x,int y) //返回当前位置能填的数字方案的二进制表示
{
    return row[x] & col[y] & cell[x/3][y/3];
}
void init() //初始化:将所有行、列、九宫格的状态设置为1~9可填
{
    for(int i = 0;i<N;i++)
    {
        col[i] = row[i] = (1<<N) - 1;
    }
    for(int i = 0;i<3;i++)
    {
        for(int j = 0;j<3;j++)
        {
            cell[i][j] = (1<<N) - 1;
        }
    }
}
void draw(int x,int y,int t,bool f) //在位置(x,y)上填数字t(f = 1) or 删去t(f = 0) 
{
    //对字符串进行实际的填入或删除操作
    if(f) str[x*N + y] = t + '1'; 
    else str[x*N + y] = '.';
    
    //更新对应行、列、九宫格的状态
    int v = 1<<t;
    if(f)
    {
        row[x] -= v;
        col[y] -= v;
        cell[x/3][y/3] -= v;
    }
    else
    {
        row[x] += v;
        col[y] += v;
        cell[x/3][y/3] += v;
    }
}
bool dfs(int cnt)
{
    if(cnt == 0) return true;
    
    //优化操作:找到可填数字最少的格子(分支数最少)
    int minv = 10;
    int minx,miny;
    int k = 0;
    for(int x = 0;x<N;x++)
    {
        for(int y = 0;y<N;y++,k++)
        {
            if(str[k] == '.') //为空
            {
                int state = get(x,y); //得到方案的二进制表示
                if(ones[state] < minv) //方案中几个1表示有几个数字可填
                {
                    minv = ones[state];
                    minx = x,miny = y;    
                }
            }
        }
    }
    
    //从分支最少的方案开始搜
    int state = get(minx,miny);
    for(int i = state;i;i -= lowbit(i)) //枚举状态中所有1
    {
        int t = map[lowbit(i)]; //取出1所表示的能放的数字
        draw(minx,miny,t,1); //放入该数字
        if(dfs(cnt-1)) return true; //搜索下一个位置,若成功放置,返回true
        draw(minx,miny,t,0);//恢复现场
    }
    
    return false; //找不到任何方案,返回false
}
int main()
{
    for(int i = 0;i<N;i++) //预处理:lowbit返回的是2的次幂,因此需要求出2的几次幂 map(x) = log2(x)
    {
        map[1<<i] = i;
    }
    for(int i = 0;i<M;i++) //预处理:一个状态里面有多少个1
    {
        for(int j = 0;j<N;j++)
        {
            ones[i] += i >> j & 1;
        }
    }
    
    while(cin>>str,str[0] != 'e')
    {
        init();//初始化
        
        int cnt = 0; //空格子的个数
        int k = 0;//枚举所有格子
        for(int x = 0;x<N;x++)//x,y表示位置
        {
            for(int y = 0;y<N;y++,k++) 
            {
                if(str[k] != '.')
                {
                    int t = str[k] - '1'; //注意:减去'1'
                    draw(x,y,t,1); //根据给定格子的数更新状态
                }
                else ++cnt;
            }
        }
        
        dfs(cnt);
        puts(str);
    }
    return 0;
}

木棒

image

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 70;
int sum,n,Max;
int a[N];
int len; 
bool st[N]; //第i根小棒是否被使用

bool dfs(int cnt,int start,int cur) //cnt:当前组装的长度为len的大棒个数,start为枚举起点
{
    if(cnt*len == sum) return true; //搜索到结果
    if(cur == len) return dfs(cnt+1,0,0);//当前大棒已组成,组装下一根
    
    //剪枝1:排除等效冗余,从start开始枚举
    for(int i = start;i<n;i++)
    {
        if(st[i]) continue;//使用过的不用
        if(cur + a[i] > len) continue; //剪枝2:可行性剪枝,超长度的不用
        st[i] = 1; //标记已使用
        if(dfs(cnt,i+1,cur + a[i])) return true; //若能组装
        
        //若不能组装
        st[i] = 0;//恢复现场
        if(cur == 0) return false; //剪枝3:可行性剪枝,当前第一根小棒都加入失败,则一定失败
        if(cur + a[i] == len) return false; //剪枝4:可行性剪枝,当前最后一根不同长度的小棒加入失败,则一定失败
        
        int j;
        for(j = i;j<n && a[j] == a[i];j++);//剪枝5:可行性剪枝,当前长度小棒加入失败,则后面相同的也一定失败
        i = j - 1;
    }
    return false;
}
int main()
{
    while(cin>>n,n)
    {
        memset(st, 0, sizeof st);
        sum = 0,Max = 0;
        
        for(int i = 0;i<n;i++)
        {
            cin>>a[i];
            Max = max(Max,a[i]);//每个相同小棒的长度必须大于给定小棒的最大值
            sum += a[i];
        }
        sort(a,a+n);//优化搜索顺序:降序排序,减少搜索分支
        reverse(a,a+n);
        for(len = Max;;len++)//优化搜索顺序:从最大值开始枚举
        {
            if(sum % len == 0 && dfs(0,0,0))
            {
                cout<<len<<endl;
                break;
            }
        }
    }
    return 0;
}

168. 生日蛋糕

image

标签:第二章,return,int,DFS,st,算法,dfs,ans,include
来源: https://www.cnblogs.com/zjq182/p/16383611.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有