2020牛客寒假算法基础集训营3(2020.2.8)

2020牛客寒假算法基础集训营3

  • Accepted
  • A - 牛牛的DRB迷宫I
  • I - 牛牛的汉诺塔
  • G - 牛牛的Link Power II
  • 牛客题解

Accepted

  • A - 牛牛的DRB迷宫I
  • B - 牛牛的DRB迷宫II
  • C - 牛牛的数组越位
  • D - 牛牛与二叉树的数组存储
  • E - 牛牛的随机数
  • F - 牛牛的Link Power I
  • G - 牛牛的Link Power II
  • H - 牛牛的k合因子数
  • I - 牛牛的汉诺塔
  • J - 牛牛的宝可梦Go

比赛地址:https://ac.nowcoder.com/acm/contest/3004

A - 牛牛的DRB迷宫I

2020牛客寒假算法基础集训营3(2020.2.8)_第1张图片
解题思路:
我用的方法是DP,也可以写成递归式的记忆化搜索(详见牛客题解)。 m p [ i ] [ j ] mp[i][j] mp[i][j] 只能往右走或者往下走,所以 m p [ i ] [ j ] mp[i][j] mp[i][j] m p [ i ] [ j + 1 ] mp[i][j+1] mp[i][j+1] m p [ i + 1 ] [ j ] mp[i+1][j] mp[i+1][j] 有贡献,状态转移方程:

if (mp[i][j] == 'D')
	dp[i + 1][j] = (dp[i + 1][j] + dp[i][j]) % mod;
if (mp[i][j] == 'R')
	dp[i][j + 1] = (dp[i][j + 1] + dp[i][j]) % mod;
if (mp[i][j] == 'B')
	dp[i + 1][j] = (dp[i + 1][j] + dp[i][j]) % mod, dp[i][j + 1] = (dp[i][j + 1] + dp[i][j]) % mod;

Code:

#include 
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 55;
int n, m;
char mp[N][N];
ll dp[N][N];
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i)
        scanf("%s", mp[i] + 1);
    dp[1][1] = 1;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            if (mp[i][j] == 'D')
                dp[i + 1][j] = (dp[i + 1][j] + dp[i][j]) % mod;
            if (mp[i][j] == 'R')
                dp[i][j + 1] = (dp[i][j + 1] + dp[i][j]) % mod;
            if (mp[i][j] == 'B') {
                dp[i + 1][j] = (dp[i + 1][j] + dp[i][j]) % mod;
                dp[i][j + 1] = (dp[i][j + 1] + dp[i][j]) % mod;
            }
        }
    }
    printf("%lld\n", dp[n][m]);
    return 0;
}

I - 牛牛的汉诺塔

2020牛客寒假算法基础集训营3(2020.2.8)_第2张图片
2020牛客寒假算法基础集训营3(2020.2.8)_第3张图片
2020牛客寒假算法基础集训营3(2020.2.8)_第4张图片
解题思路:
汉诺塔……`(>﹏<)′。首先说一个汉诺塔的移动次数 ( 2 n − 1 ) (2^n-1) (2n1) 。本题可以记忆化搜索,也可以递推。
先看 经典汉诺塔问题Code,对应到此题就是求六种转移方式的移动次数:

#include 
using namespace std;
int n;
int sum[3][3];                         //sum[i][j]从i柱移动到j柱
void hanoi(int a, int b, int c, int n) //第n个盘从a通过b移动到c上
{
    if (n == 1)
    {
        sum[a][c]++;
        return;
    }
    hanoi(a, c, b, n - 1);
    sum[a][c]++;
    hanoi(b, a, c, n - 1);
}
int main()
{
    scanf("%d", &n);
    hanoi(0, 1, 2, n);
    return 0;
}

上面方法中 s u m sum sum数组记下了每种转移方式的次数,但是这样做绝对超时!!
我们根据 h a n o i ( ) hanoi() hanoi() 函数的四个参数可以记忆化搜索
下图是我画出的5个盘子的汉诺塔函数递归树,它是一棵满二叉树,可以看出,其中有许多树节点是重复出现的,所以我们用数组 d p [ a ] [ b ] [ c ] [ n ] dp[a][b][c][n] dp[a][b][c][n] 记录下每个节点的信息,就可以大大降低时间复杂度,实现记忆化搜索:
2020牛客寒假算法基础集训营3(2020.2.8)_第5张图片

Code:

//记忆化搜索
#include 
using namespace std;
struct node
{
    long long data[6]; //六种移动方式
    node() { memset(data, 0, sizeof(data)); }
    node operator+(const node &A) const
    {
        node res;
        for (int i = 0; i < 6; i++)
        {
            res.data[i] = this->data[i] + A.data[i];
        }
        return res;
    }
};
int n;
node dp[3][3][3][66];	//记录每个递归节点的信息
bool vis[3][3][3][66];	//标记是否记录信息
void moveto(int x, int y, node &A)
{
    if (x == 0 && y == 1) //A->B    0
        ++A.data[0];
    else if (x == 0 && y == 2) //A->C    1
        ++A.data[1];
    else if (x == 1 && y == 0) //B->A    2
        ++A.data[2];
    else if (x == 1 && y == 2) //B->C    3
        ++A.data[3];
    else if (x == 2 && y == 0) //C->A    4
        ++A.data[4];
    else if (x == 2 && y == 1) //C->B    5
        ++A.data[5];
}
node hanoi(int a, int b, int c, int n) //第n个盘从a通过b移动到c上
{
    if (vis[a][b][c][n])
        return dp[a][b][c][n];
    if (n == 1)
    {
        moveto(a, c, dp[a][b][c][n]);
        vis[a][b][c][n] = true;
        return dp[a][b][c][n];
    }
    node temp;
    temp = temp + hanoi(a, c, b, n - 1);
    moveto(a, c, temp);
    temp = temp + hanoi(b, a, c, n - 1);
    vis[a][b][c][n] = true;
    dp[a][b][c][n] = temp;
    return dp[a][b][c][n];
}
int main()
{
    scanf("%d", &n);
    node ans = hanoi(0, 1, 2, n);
    printf("A->B:%lld\n", ans.data[0]);
    printf("A->C:%lld\n", ans.data[1]);
    printf("B->A:%lld\n", ans.data[2]);
    printf("B->C:%lld\n", ans.data[3]);
    printf("C->A:%lld\n", ans.data[4]);
    printf("C->B:%lld\n", ans.data[5]);
    printf("SUM:%lld\n", (1LL << n) - 1); //注意这里要写成 1LL
    return 0;
}

G - 牛牛的Link Power II

我的博客:https://blog.csdn.net/weixin_44169557/article/details/105476224

牛客题解

附 牛客网题解链接 含标程
https://ac.nowcoder.com/discuss/365306?tdsourcetag=s_pctim_aiomsg

你可能感兴趣的:(牛客练习)