DFS的一些题目

文章介绍了如何使用深度优先搜索(DFS)解决奶牛选美问题,通过DFS遍历两个连通块并计算最小匹配距离。另外,还涉及了求解大臣旅费问题(求最长路径)、利用DFS和树的直径概念优化求解,以及使用哈希处理排雷问题的代码片段。
摘要由CSDN通过智能技术生成

题目1:奶牛选美

这道题其实就是把两个连通块合成一个,可以用dfs、bfs和并查集。因为最近在dfs专题训练,这里我只写了dfs。

首先我们用dfs的方式遍历两个连通块,将两个连通块中点的坐标分别存入两个数组中,将这两个数组中的点一 一匹配,迭代出最小值。

#include<iostream>
#include<vector>
using namespace std;
typedef pair<int,int> PII;
#define x first
#define y second
vector<PII> points[2];
const int N=55;
int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
int n,m;
char g[N][N];
void dfs(int x,int y,vector<PII> &q)
{
    q.push_back({x,y});
    g[x][y]='.';
    for(int i=0;i<4;i++)
    {
        int cx=x+dx[i],cy=y+dy[i];
        if(cx<0||cx>=n||cy<0||cy>=m) continue;
        if(g[cx][cy]=='X') dfs(cx,cy,q);
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++) scanf("%s",g[i]);
    int k=0;
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
        {
            if(g[i][j]=='X') dfs(i,j,points[k++]);
        }
    int res=N*N;
    
    for(int i=0;i<points[0].size();i++)
        for(int j=0;j<points[1].size();j++)
        {
            int x1=points[0][i].x,y1=points[0][i].y;
            int x2=points[1][j].x,y2=points[1][j].y;
            int t=abs(x1-x2)+abs(y1-y2)-1;
            res=min(res,t);
        }
    printf("%d",res);
}

题目2:大臣的旅费

首先是最暴力的写法。

以每个点为起点,搜索它的最长路径,最后取所有点为起点时最长路径的最大值。

时间复杂度是O(n^{2})。

#include<iostream>
#include<cstring>
using namespace std;
const int N=2e5+10;
int h[N],e[N],ne[N],w[N],idx=0;
int n;
bool st[N];
int sum=0;
void add(int p,int q,int d)
{
    e[idx]=q;
    w[idx]=d;
    ne[idx]=h[p];
    h[p]=idx++;
}
int dfs(int u,int res)
{
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        if(st[j]) continue;
        st[j]=1;
        sum=max(sum,dfs(j,res+w[i]));
        st[j]=0;
    }
    return res;
}
int main()
{
    scanf("%d",&n);
    memset(h,-1,sizeof(h));
    for(int i=1;i<n;i++) 
    {
        int p,q,d;
        scanf("%d%d%d",&p,&q,&d);
        add(p,q,d);
        add(q,p,d);
    }
    int res=0;
    
    for(int i=1;i<=n;i++) 
    {
        memset(st,0,sizeof(st));
        st[i]=1;
        dfs(i,0);
    }
    printf("%d",((10+1)+(10+sum))*sum/2);
}

接下来考虑该如何优化。

这其实涉及到一个知识点——求树的直径。这个问题有固定的步骤:

①选定一个x,求出x到所有点的距离;

②找到与x距离最远的点y,求出y到所有点的距离;

③y到所有点的最长距离即为答案。

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=2e5+10;
int h[N],e[N],ne[N],w[N],idx=0;
int n;
bool st[N];
int dist[N];
void add(int p,int q,int d)
{
    e[idx]=q;
    w[idx]=d;
    ne[idx]=h[p];
    h[p]=idx++;
}
void dfs(int u,int di)
{
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int j=e[i];
        if(dist[j]!=-1) continue;
        dist[j]=di+w[i];
        dfs(j,dist[j]);
    }
}
int main()
{
    scanf("%d",&n);
    memset(h,-1,sizeof(h));
    for(int i=1;i<n;i++) 
    {
        int p,q,d;
        scanf("%d%d%d",&p,&q,&d);
        add(p,q,d);
        add(q,p,d);
    }
    memset(dist,-1,sizeof(dist));
    dist[1]=0;
    dfs(1,0);
    int maxpoint=0,maxval=0;
    for(int i=1;i<=n;i++)
        if(dist[i]>maxval)
        {
            maxval=dist[i];
            maxpoint=i;
        }
    memset(dist,-1,sizeof(dist));
    dist[maxpoint]=0;
    dfs(maxpoint,0);
    sort(dist+1,dist+1+n);
    int res=dist[n];
    printf("%lld",(long long)(10+1+10+res)*res/2);
    
}

题目3:扫雷

这道题的过程是:遍历排雷火箭,如果排雷火箭的范围(遍历-r到r之间在圆内的所有点)波及到了炸弹,则炸弹爆炸,dfs炸弹的范围。

这道题过完所有数据需要手写哈希,这里没有手写,过了8/11个数据。

#include<iostream>
#include<unordered_map>
using namespace std;
const int N=5e4+10,M=1e9+1;
typedef pair<int,int> PII;
typedef long long ll;
#define x first
#define y second
int n,m;
pair<PII,int> jian[N],lei[N];
unordered_map<ll,int> is_lei;
unordered_map<ll,int> num;
int dx[8]={-1,-1,-1,0,0,1,1,1};
int dy[8]={-1,0,1,-1,1,-1,0,1};
int cnt=0;
void dfs(int x,int y,int r)
{
    for(int cx=max(0,x-r);cx<=x+r;cx++)
        for(int cy=max(0,y-r);cy<=y+r;cy++)
        {
            if((cx-x)*(cx-x)+(cy-y)*(cy-y)>r*r) continue; 
            ll t;
            t=cx*M+cy;
            
            if(is_lei.count(t)) 
            {
                cnt+=num[t];
                int tr=is_lei[t];
                is_lei.erase(t);
                num.erase(t);
                dfs(cx,cy,tr);
            }
        }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d%d%d",&lei[i].x.x,&lei[i].x.y,&lei[i].y);
    for(int i=1;i<=n;i++) 
    {
        ll t;
        t=lei[i].x.x*M+lei[i].x.y;
        
        if(is_lei.count(t)) 
        {
            is_lei[t]=max(is_lei[t],lei[i].y);
            num[t]++;
        }
        else 
        {
            is_lei[t]=lei[i].y;
            num[t]=1;
        }
    }
    for(int i=1;i<=m;i++) scanf("%d%d%d",&jian[i].x.x,&jian[i].x.y,&jian[i].y);
    for(int i=1;i<=m;i++) dfs(jian[i].x.x,jian[i].x.y,jian[i].y);
    printf("%d",cnt);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值