C++:dfs习题四则

1. 带分数

1209. 带分数 - AcWing题库

100100 可以表示为带分数的形式:100=3+69258714100=3+69258714

还可以表示为:100=82+3546197100=82+3546197

注意特征:带分数中,数字 1∼91∼9 分别出现且只出现一次(不包含 00)。

类似这样的带分数,100100 有 1111 种表示法。

输入格式

一个正整数。

输出格式

输出输入数字用数码 1∼91∼9 不重复不遗漏地组成带分数表示的全部种数。

数据范围

1≤N<106

输入:

100

输出:

11

输入:

105

输出:

6

思路

用dfs_a计算出整数部分,在根据这个整数来用dfs_c枚举出分数的分母,我们知道这个式子的和,这样就可以求出分子,只要看分子用的所有数字是否从来没有被用过即可。

AC代码
cpp 复制代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

int N;
int count1=0;
bool state[21]={false};
bool je[21]={false};

bool check(int a,int c)
{
    int b=(N-a)*c;//三个未知数枚举出两个数就能求第三个数
    if(!b||!a||!c)//有0
    return false;
    memcpy(je,state,sizeof(state));
    while(b)
    {
        int t=b%10;
        b/=10;
        if(t==0||je[t])//为0或者使用过
        return false;
        je[t]=true;
    }
    for(int i=1;i<10;i++)
    if(!je[i])
    return false;
    
    return true;
}

void dfs_c(int u,int a,int c)
{
    if(u>=9)//不可能分母已经选了九个了
    return;
    if(check(a,c))
    count1++;
    for(int i=1;i<10;i++)
    {
        if(!state[i])
        {
            state[i]=true;
            dfs_c(u+1,a,c*10+i);//c是选择分母
            state[i]=false;
        }
    }
}
//u表示选择了几个数
void dfs_a(int u,int a)//枚举整数
{
    if(a>=N)
    return ;
    if(a)//a不是0
        dfs_c(u,a,0);
    
    for(int i=1;i<10;i++)
    {
        if(!state[i])
        {
            state[i]=true;
            
            dfs_a(u+1,a*10+i);
            
            state[i]=false;
        }
    }
}

int main()
{
    scanf("%d", &N);
    dfs_a(1, 0);
    printf("%d", count1);
    return 0;
}

2. 奶牛选美

2060. 奶牛选美 - AcWing题库

听说最近两斑点的奶牛最受欢迎,约翰立即购进了一批两斑点牛。

不幸的是,时尚潮流往往变化很快,当前最受欢迎的牛变成了一斑点牛。

约翰希望通过给每头奶牛涂色,使得它们身上的两个斑点能够合为一个斑点,让它们能够更加时尚。

牛皮可用一个 N×MN×M 的字符矩阵来表示,如下所示:

................
..XXXX....XXX...
...XXXX....XX...
.XXXX......XXX..
........XXXXX...
.........XXX....

其中,XX 表示斑点部分。

如果两个 XX 在垂直或水平方向上相邻(对角相邻不算在内),则它们属于同一个斑点,由此看出上图中恰好有两个斑点。

约翰牛群里所有的牛都有两个斑点

约翰希望通过使用油漆给奶牛尽可能少的区域内涂色,将两个斑点合为一个。

在上面的例子中,他只需要给三个 .. 区域内涂色即可(新涂色区域用 ∗∗ 表示):

................
..XXXX....XXX...
...XXXX*...XX...
.XXXX..**..XXX..
........XXXXX...
.........XXX....

请帮助约翰确定,为了使两个斑点合为一个,他需要涂色区域的最少数量。

输入格式

第一行包含两个整数 NN 和 MM。

接下来 NN 行,每行包含一个长度为 MM 的由 XX 和 .. 构成的字符串,用来表示描述牛皮图案的字符矩阵。

输出格式

输出需要涂色区域的最少数量。

输入:

6 16
................
..XXXX....XXX...
...XXXX....XX...
.XXXX......XXX..
........XXXXX...
.........XXX....

输出:

3

思路

将两个斑点分别存储到两个数组中。再写一个gat函数计算两个坐标之间的最短距离,分别计算第一个斑点的每个点到最大斑点的最小距离,用一个变量记录下来。

AC代码
cpp 复制代码
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue>
using namespace std;

int dx[5]={0,0,1,-1};
int dy[5]={1,-1,0,0};

int cntmin=1e9;
int n,m;
char map[55][55];
bool st[55][55];//其中一个斑点
bool je[55][55];//并非斑点
vector<pair<int,int>> v1;//第一个斑点
vector<pair<int,int>> v2;//第二个斑点
void bfs_st(int x,int y,vector<pair<int,int>>& v)
{
    queue<pair<int,int>> qp;
    qp.push({x,y});
    v.push_back({x,y});
    st[x][y]=true;
    while(!qp.empty())
    {
        int x1=qp.front().first;
        int y1=qp.front().second;
        qp.pop();
        for(int i=0;i<4;i++)
        {
            int x2=x1+dx[i];
            int y2=y1+dy[i];
            if(x2<0||y2<0||x2>=n||y2>=m||map[x2][y2]!='X'||st[x2][y2])
            continue;
            st[x2][y2]=true;            
            v.push_back({x2,y2});                          
            qp.push({x2,y2});
        }
    }
}


int get(pair<int,int>& p1,pair<int,int>& p2)
{
   return abs(p1.first-p2.first)+abs(p1.second-p2.second)-1; 
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++)
    {
        cin>>map[i];
    }
    
    bool f=false;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            if(map[i][j]=='X'&&!f)//只进行一次
            {
                bfs_st(i,j,v1);
                f=true;
            }
            if(map[i][j]=='X'&&!st[i][j]&&f)
            {
                bfs_st(i,j,v2);
            }
        }
    }
    for(int i=0;i<v1.size();i++)
    {
        for(int j=0;j<v2.size();j++)
        {
            cntmin=min(cntmin,get(v1[i],v2[j]));
        }
        
    }
    printf("%d\n",cntmin);
    
    return 0;
}

3. 大臣的旅费

1207. 大臣的旅费 - AcWing题库

很久以前,TT 王国空前繁荣。

为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市。

为节省经费,TT 国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者通过其他大城市间接到达。

同时,如果不重复经过大城市,从首都到达每个大城市的方案都是唯一的。

JJ 是 TT 国重要大臣,他巡查于各大城市之间,体察民情。

所以,从一个城市马不停蹄地到另一个城市成了 JJ 最常做的事情。

他有一个钱袋,用于存放往来城市间的路费。

聪明的 JJ 发现,如果不在某个城市停下来修整,在连续行进过程中,他所花的路费与他已走过的距离有关。

具体来说,一段连续的旅途里,第 11 千米的花费为 1111,第 22 千米的花费为 1212,第 33 千米的花费为 1313,...,第 xx 千米的花费为 x+10x+10。

也就是说,如果一段旅途的总长度为 11 千米,则刚好需要花费 1111,如果一段旅途的总长度为 22 千米,则第 11 千米花费 1111,第 22 千米花费 1212,一共需要花费 11+12=2311+12=23。

JJ 大臣想知道:他从某一个城市出发,中间不休息,到达另一个城市,所有可能花费的路费中最多是多少呢?

输入格式

输入的第一行包含一个整数 nn,表示包括首都在内的 TT 王国的城市数。

城市从 11 开始依次编号,11 号城市为首都。

接下来 n−1n−1 行,描述 TT 国的高速路(TT 国的高速路一定是 n−1n−1 条)。

每行三个整数 Pi,Qi,DiPi,Qi,Di,表示城市 PiPi 和城市 QiQi 之间有一条双向高速路,长度为 DiDi 千米。

输出格式

输出一个整数,表示大臣 JJ 最多花费的路费是多少。

数据范围

1≤n≤1051≤n≤105,

1≤Pi,Qi≤n1≤Pi,Qi≤n,

1≤Di≤1000

输入:

5
1 2 2
1 3 1
2 4 5
2 5 4

输出:

135

思路

因为不会重复经过大城市所以这是一个多叉树,我们要求的最大值就是求树的直径了。 先随便取一个点(城市)a到离这个点最远的点(城市)b。在从新找到这个点b出发找到离这个b点最远的点c,点c就是花费的最大路费(为什么?反证法:如果b到c不是最大路径,那真正的直径的两个端点之一应该离a更远,与b是离a最远的点矛盾了),

具体操作:用一个数组v[i]将i这个城市所有能通向的城市与距离记录下来,dfs三个参数(1.从哪个城市走2. 当前城市是从哪个城市来的防止走回头路3. 从最开始的城市到当前这个城市所用的距离)用来遍历这个城市所有可能到的城市(除了回头路),用sum记录下来离我们传出最远的距离,id记录这个距离的城市。

再用得到的id为起点寻找(将sum清空)离这个位置最远的城市,sum记录下来距离,根据题目给出的费用计算规则计算输出即可。

AC代码
cpp 复制代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int N=1e5+10;
int n;
vector<pair<int,int>> v[N]; 

long long sum=0;
int id;

long long add(long long x)
{
    int f=1;
    long long tmp=0;
    while(x--)
    {
        tmp+=10+f;
        f++;
    }
    return tmp;
}

void dfs(int a,int father,int cnt)
{
    if(cnt>sum)
    {
        sum=cnt;
        id=a;
    }
    for(auto tmp:v[a])
    {
        if(tmp.first!=father)//没走过
        {
            dfs(tmp.first,a,cnt+tmp.second);
        }
    }
}

int main()
{
    scanf("%d",&n);
    for(int i=0;i<n-1;i++)
    {
        int a,b,d;
        scanf("%d%d%d",&a,&b,&d);
        v[a].push_back({b,d});//a这个城市能到b距离是d
        v[b].push_back({a,d});//b这个城市能到a距离是d
    }
    
    dfs(1,-1,0);
    sum=0;
    dfs(id,-1,0);
    
    printf("%lld",add(sum));
    
    
    return 0;
}

4. 扫雷

4407. 扫雷 - AcWing题库

小明最近迷上了一款名为《扫雷》的游戏。

其中有一个关卡的任务如下:

在一个二维平面上放置着 nn 个炸雷,第 ii 个炸雷 (xi,yi,ri)(xi,yi,ri) 表示在坐标 (xi,yi)(xi,yi) 处存在一个炸雷,它的爆炸范围是以半径为 riri 的一个圆。

为了顺利通过这片土地,需要玩家进行排雷。

玩家可以发射 mm 个排雷火箭,小明已经规划好了每个排雷火箭的发射方向,第 jj 个排雷火箭 (xj,yj,rj)(xj,yj,rj) 表示这个排雷火箭将会在 (xj,yj)(xj,yj) 处爆炸,它的爆炸范围是以半径为 rjrj 的一个圆,在其爆炸范围内的炸雷会被引爆。

同时,当炸雷被引爆时,在其爆炸范围内的炸雷也会被引爆。

现在小明想知道他这次共引爆了几颗炸雷?

你可以把炸雷和排雷火箭都视为平面上的一个点。

一个点处可以存在多个炸雷和排雷火箭。

当炸雷位于爆炸范围的边界上时也会被引爆。

输入格式

输入的第一行包含两个整数 n、mn、m。

接下来的 nn 行,每行三个整数 xi,yi,rixi,yi,ri,表示一个炸雷的信息。

再接下来的 mm 行,每行三个整数 xj,yj,rjxj,yj,rj,表示一个排雷火箭的信息。

输出格式

输出一个整数表示答案。

数据范围

对于 40%40% 的评测用例:0≤x,y≤109,0≤n,m≤103,1≤r≤100≤x,y≤109,0≤n,m≤103,1≤r≤10,

对于 100%100% 的评测用例:0≤x,y≤109,0≤n,m≤5×104,1≤r≤100≤x,y≤109,0≤n,m≤5×104,1≤r≤10。

思路

将二维坐标转化成一维坐标,然后使用线性探测法将其维护到哈希表中,要维护三个哈希表,分别是hash1里面存储二维转化成的一维数字,用来维护线性探测法的(因为有可能其他数字%M后与其重复,减少重复可能),hash2记录这个点有多少个地雷,hash3记录这个点所有地雷的最大半径。

dfs参数有三个(当前这个点的横纵坐标与半径)我们将所有排雷火箭横纵坐标与爆炸半径传入通过枚举这个点周围x-r到x+r与y-r到y+r的每一个点,转换为一维后看这个点是否在爆炸范围内,如果在就将其转化成一维数字,并将这个点记录下来(已经引爆了)将这个点的所有地雷累加到计数爆炸地雷的变量sum,然后递归这个位置地雷的最大爆炸半径能引爆多少地雷。

最后输出sum计数即可

AC代码
cpp 复制代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

const int N = 1e5 + 10, M = 1e5 + 1;
typedef long long ll;
ll be=1e9+100;//be大一点,防止重复
int n,m;
int hash1[N];//维护find函数,
int hash2[N];//这个点地雷数量
int hash3[N];//这个点地雷的最大半径
bool st[N];
ll get(int x,int y)//二维坐标转换成一维坐标
{
    return (ll)x*be+y;
}

int find(int x)
{
    int t=(x%M+M)%M;
    while(hash1[t]!=-1&&hash1[t]!=x)//被使用了,并且不是那个点(二维转化成一位的点)
    {
        t++;
        if(t==M) t=0;
    }
    return t;
}
int sum;
bool judge(int x1, int y1, int x2, int y2, int r)//判断x2y2是否在圆心为x1y1的圆内
{
    int t = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
    return t <= r * r;
}
void dfs(int x,int y ,int r)
{
    for(int i=-r;i<=r;i++)//横轴
    {
        for(int j=-r;j<=r;j++)//纵轴
        {
            int dx=x+i;
            int dy=y+j;
            if(judge(x,y,dx,dy,r))
            {
                int t=get(dx,dy);
                if(hash1[find(t)]!=-1&&!st[find(t)])
                {
                    sum+=hash2[find(t)];
                    st[find(t)]=true;
                    dfs(dx,dy,hash3[find(t)]);//炸弹
                }
            }
        }
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    memset(hash1,-1,sizeof(hash1));
    for(int i=0;i<n;i++)
    {
        int x,y,r;
        scanf("%d%d%d",&x,&y,&r);
        int t=get(x,y);//二维转一维
        // cout<<t<<"   "<<endl;
        int tmp=find(t);
        hash1[tmp]=t;
        hash2[tmp]++;
        hash3[tmp]=max(hash3[tmp],r);//记录最大半径

    }
    for(int i=0;i<m;i++)
    {
        int x,y,r;
        scanf("%d%d%d",&x,&y,&r);
        dfs(x,y,r);
    }
    printf("%d\n",sum);
    return 0;
}

这篇就到这里了,(๑′ᴗ‵๑)I Lᵒᵛᵉᵧₒᵤ❤

相关推荐
mit6.8242 小时前
[实现Rpc] 通信类抽象层 | function | using | 解耦合设计思想
c++·网络协议·rpc
laimaxgg2 小时前
Qt常用控件之单选按钮QRadioButton
开发语言·c++·qt·ui·qt5
尼尔森系3 小时前
排序与算法:希尔排序
c语言·算法·排序算法
AC使者4 小时前
A. C05.L08.贪心算法入门
算法·贪心算法
冠位观测者4 小时前
【Leetcode 每日一题】624. 数组列表中的最大距离
数据结构·算法·leetcode
yadanuof4 小时前
leetcode hot100 滑动窗口&子串
算法·leetcode
可爱de艺艺4 小时前
Go入门之函数
算法
ox00805 小时前
C++ 设计模式-命令模式
c++·设计模式·命令模式
武乐乐~5 小时前
欢乐力扣:旋转图像
算法·leetcode·职场和发展
Blasit5 小时前
C++ Qt建立一个HTTP服务器
服务器·开发语言·c++·qt·http