比赛地址:https://ac.nowcoder.com/acm/contest/3004
解题思路:
我用的方法是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;
}
解题思路:
汉诺塔……`(>﹏<)′。首先说一个汉诺塔的移动次数 ( 2 n − 1 ) (2^n-1) (2n−1) 。本题可以记忆化搜索,也可以递推。
先看 经典汉诺塔问题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] 记录下每个节点的信息,就可以大大降低时间复杂度,实现记忆化搜索:
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;
}
我的博客:https://blog.csdn.net/weixin_44169557/article/details/105476224
附 牛客网题解链接 含标程
https://ac.nowcoder.com/discuss/365306?tdsourcetag=s_pctim_aiomsg