NUEQ-ACM2022预备队第二次双周赛

第二次双周赛

第一题

题目1

输出全排列

思路1

使用dfs

代码1

#include<iostream>
using namespace std;

int n;
//是否访问过
bool visited[10];

//s为这个排列的字符串
void dfs(int c, string s)
{
    //全部访问过了
    if (c == n)
    {
        //输出
        cout << s << endl;
    }
    //遍历
    for (int i = 1; i <= n; i++)
    {
        if (!visited[i])
        {
            //标记、进行下一次dfs、取消标记
            visited[i] = true;
            dfs(c + 1, s + (char)(i + '0'));
            visited[i]  = false;
        }
    }
}

int main()
{
    cin >> n;

    //对每个数进行一次dfs,得到这个数开头的排列
    for (int i = 1; i <= n; i++)
    {
        visited[i] = true;
        //初始字符串
        string s = "";
        dfs(1, s + (char)(i + '0'));
        visited[i] = false;
    }
    return 0;
}

第二题

题目2

给一张地图,有部分为山峰(1),有部分为平地(0),求山的个数(上下左右连在一起的山峰)

思路2

对每个点用dfs(不需要取消标记)或者bfs,将这个点所在的山内所有1都标记为访问过

代码2

#include<iostream>
#include<utility>
#include<queue>
using namespace std;

//m、n、山个数
int m, n, cnt = 0;
//标记
bool visited[2001][2001];
//地图
bool mp[2001][2001];
//四个方向对应在x和y上的偏移量
int xoffset[] = {0, 1, 0, -1}, yoffset[] = {1, 0, -1, 0};
//bfs的队列
queue<pair<int, int> > q;

int main()
{
    cin >> m >> n;
    for (int i = 1; i <= m; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            cin >> mp[i][j];
        }
    }

    //遍历每个点
    for (int i = 1; i <= m; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            //当这个点没有被标记为别的山的一部分并且这个点是山峰,则进行bfs
            if (!visited[i][j] && mp[i][j])
            {
                //进行一次bfs就代表找到一个山
                cnt++;
                //入队列准备搜索
                q.push(make_pair(i, j));
                //标记
                visited[i][j] = true;
                //bfs
                while (!q.empty())
                {
                    //当前点坐标
                    int x = q.front().first, y = q.front().second;
                    //出队列
                    q.pop();
                    //在四个方向上找下一个点
                    for (int k = 0; k < 4; k++)
                    {
                        int nextx = x + xoffset[k], nexty = y + yoffset[k];
                        //在图内并且是山峰并且还没访问过
                        if (nextx >= 1 && nexty >= 1 && nextx <= m && nexty <= n && mp[nextx][nexty] && !visited[nextx][nexty])
                        {
                            //入队列
                            q.push(make_pair(nextx, nexty));
                            //标记
                            visited[nextx][nexty] = true;
                        }
                    }
                }
            }
        }
    }
    cout << cnt << endl;
    return 0;
}

第三题

题目3

有一条充满数字的方块路由一个非负的整数数组m组成,最开始的位置在下标start,当位于下标i位置时可以向前或者向后跳跃m[i]步数,已知元素值为0处的位置是出口,问是否可以到达出口

思路3

用dfs或bfs从起点按规则搜索,如果到得了0就能到达

代码3

#include<iostream>
#include<queue>
using namespace std;

//n和起点坐标
int n, start;
//bfs是否访问过
bool visited[50001];
//数组m
int mp[50001];
//bfs队列
queue<int> q;

int main()
{
    cin >> n;
    for (int i = 0; i < n; i++)
    {
        cin >> mp[i];
    }
    cin >> start;

    //将起点标记并入队列
    visited[start] = true;
    q.push(start);

    //指示是否可行
    bool isPossible = false;
    //bfs
    while (!q.empty())
    {
        //当前点
        int p = q.front();
        //出队列
        q.pop();
        //到出口了
        if (mp[p] == 0)
        {
            //输出
            cout << "True" << endl;
            //更新标记
            isPossible = true;
            //提前终止bfs
            break;
        }

        //向后的点位置、向前的点位置
        int next1 = p + mp[p], next2 = p - mp[p];
        //在路上并且没访问过
        if (next1 >= 0 && next1 < n && !visited[next1])
        {
            //入队列并标记
            q.push(next1);
            visited[next1] = true;
        }
        //在路上并且没访问过
        if (next2 >= 0 && next2 < n&& !visited[next2])
        {
            //入队列并标记
            q.push(next2);
            visited[next2] = true;
        }
    }
    //如果标记没有被更新,则到不了出口
    if (!isPossible)
    {
        cout << "False" << endl;
    }

    return 0;
}

第四题

题目4

给一个数n,求出区间[1e8, n]中所有的回文数

思路4

数据非常大,直接暴力遍历肯定超时。
可以考虑生成所有的回文数,再计数。回文数的个数较少,更容易完成。
由于题目保证了数字为9为,则可以先选定中间数字,再向两边添加相同的数字4次,就可以生成一个回文数。
放在搜索的考试里,肯定是用搜索。用dfs得到所有的情况。

代码4

#include<iostream>
using namespace std;

//回文数每一位的数字
int b[9];
//n、回文数个数
int n, cnt = 0;

//判断生成的9位回文数是否在给定范围内
bool isInRange()
{
    //将b数组中的每一位组合成一个数
    int t = 0;
    for (int i = 0; i < 9; i++)
    {
        t *= 10;
        t += b[i];
    }
    //判断
    if (t >= 1e8 && t <= n)
    {
        return true;
    }
    else
    {
        return false;
    }
}

//step为生成次数
void dfs(int step)
{
    //如果是第五次(已经生成了4次),就生成结束
    if (step == 5)
    {
        //如果在范围内,就可以计数
        if (isInRange())
        {
            cnt++;
        }
        return;
    }
    //添加第1、2、3次和第4次不同,第1、2、3次可以使用任何数字而第4次不能用0
    //start是遍历所有的数字的起点
    int start;
    //第4次就从1开始
    if (step == 4)
    {
        start = 1;
    }
    //其他时候就从0开始
    else
    {
        start = 0;
    }
    for (int i = start; i <= 9; i++)
    {
        //填数字
        b[4 - step] = b[4 + step] = i;                 // 0 1 2 3 4 5 6 7 8
        //下一次搜索
        dfs(step + 1);
    }
}

int main()
{
    cin >> n;
    
    //填中间数字和开始搜索
    for (int i = 0; i <= 9; i++)
    {
        b[4] = i;
        dfs(1);
    }

    cout << cnt << endl;
    return 0;
}

第五题

题目5

有一个盒子,里面有n*m个格子。有些格子里是平面镜,根据平面镜的方向会改变光线的方向。有些格子会吸收光线。其他格子对光线没有影响。现在一个地方有光源,向某个方向发射光线。小明调整光源的方向,想得到最酷的光路(环形光路最酷,其他情况越长越酷)

思路5

用dfs模拟光线的行进,直到出现了循环或者光线被吸收或光线离开盒子。
出现循环的判断方法:光线从一个方向射入了一个格子两次。
为了处理循环的特殊情况,只用二维的标记数字是不够的(从不同方向进入同一个格子不算循环),所以给标记数组加一维记录方向。

代码5

#include<iostream>
#include<string.h>
using namespace std;

//方向的定义:0-U, 1-R, 2-D, 3-L,按照题给的优先级顺序
//n、m、起始点坐标、起始光源方向
int n, m, sx, sy, startdirection;
//地图情况
char mp[501][501];
//标记数组,最后一位记录方向
bool visited[501][501][4];
//光线在某个方向上前进一个对应的x和y方向的位移
int directionx[] = {-1, 0, 1, 0}, directiony[] = {0, 1, 0, -1};
//四个方向的长度
int ans[4];
//是否出现了循环
bool isCool = false;
//如果出现了循环,出现循环的起始方向
int cooldirection;
//每个方向对应的字符表示
char directionchar[] = {'U', 'R', 'D', 'L'};

//处理平面镜反射光线的情况,根据mirror,返回direction方向的光线经过反射的方向
int changeDirection(char mirror, int direction)
{
    if (mirror == '\\')
    {
        switch (direction)
        {
            case 0:
                return 3;
            case 1:
                return 2;
            case 2:
                return 1;
            case 3:
                return 0;
        }
    }
    else if (mirror == '/')
    {
        switch (direction)
        {
            case 0:
                return 1;
            case 1:
                return 0;
            case 2:
                return 3;
            case 3:
                return 2;
        }
    }
    return -1;
}

//dfs 参数:当前坐标x和y、当前方向、当前经过了格子数
void dfs(int x, int y, int direction, int step)
{
    //如果光线离开盒子或者被吸收了,就可以记录该方向的长度并结束dfs
    if (x <= 0 || y <= 0 || x > n || y > m || mp[x][y] == 'C')
    {
        ans[startdirection] = step;
        return;
    }
    //从一个方向经过一个格子两次-循环。记录相关的数据,并结束dfs
    if (visited[x][y][direction])
    {
        isCool = true;
        cooldirection = startdirection;
        return;
    }
    //是镜子,则用刚刚的函数处理方向
    if (mp[x][y] == '\\'  || mp[x][y] == '/')
    {
        visited[x][y][direction] = true;
        direction = changeDirection(mp[x][y], direction);
    }
    //什么都没有
    if (mp[x][y] == '.')
    {
        visited[x][y][direction] = true;
    }
    //下一次dfs
    dfs(x + directionx[direction], y + directiony[direction], direction, step + 1);
}

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <=m; j++)
        {
            cin >> mp[i][j];
        }
    }
    cin >> sx >> sy;

    //循环四次方向,对每个方向都dfs一次
    for (int i = 0; i < 4; i++)
    {
        //清楚标记数据
        memset(visited, 0, sizeof(visited));
        //设置起始方向
        startdirection = i;
        /dfs
        dfs(sx, sy, startdirection, 0);
        //如果循环了,就不需要再考虑剩下的情况了,因为方向顺序按照题给的优先级,剩下的情况优先级更低
        if (isCool)
        {
            break;
        }
    }

    //有循环的情况
    if (isCool)
    {
        cout << directionchar[cooldirection] << endl;
        cout << "COOL" << endl;
    }
    //都是有限格数的情况
    else
    {
        //求最大值和最大值对应的方向
        int max = ans[0], maxi = 0;
        for (int i = 1; i < 4; i++)
        {
            if (ans[i] > max)
            {
                max = ans[i];
                maxi = i;
            }
        }
        cout << directionchar[maxi] << endl;
        cout << max << endl;
    }

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值