说在最前面

众所周知, NOIP pj 的第三题大部分都是 dp ,但是有可能在考场上想不到动态转移方程,所以我们就可以拿深搜骗分。

方法

  1. 深搜拿部分分

    • 剪枝
    • 记忆化
  2. 看数据范围

有时候发现,写完深搜,发现可以打表qwq!

那不就很香嘛(

实践出真知

例一:P1057 传球游戏

\(\Large\rm Link\)

法1

dfs 暴搜

期望得分:\(\rm 40pts\)

首先写出 \(dfs\) 的参数:

首先是小蛮在第几号,当然是 \(1\) ,然后是次数 \(0\)

再看递归边界

这里是环状递归边界:

if (x == 0) x = n;
if (x == n + 1) x = 1;
if (step == m) {
if (x == 1) return 1;
return 0;
}

接下来往下继续搜。

dfs(x + 1, step + 1) + dfs(x - 1, step + 1);

好! \(\rm 40pts\) 到手!

那么我们可以再看一下数据范围,那么小!直接打表啊!

因为时间关系,这里打表就不多讲解了。

法2

加上记忆化

期望得分:\(\rm 90pts\)

大家应该都知道:暴搜加上记忆化 \(≈\) 动归

所以我们加上记忆化:

定义一个 \(a\) 数组,表示在某一个位置经过 \(step\) 步能否回到起始位置的方法数。

if (a[x][step] != 0) return a[x][step];

放上 dfs 代码:

int dfs (int x, int step) {
if (x == 0) x = n;
if (x == n + 1) x = 1;
if (step == m) {
if (x == 1) return 1;
return 0;
}
return dfs(x + 1, step + 1) + dfs(x - 1, step + 1);
}

为什么是 90 分???

因为想一下,如果是奇数,那么永远传不到小蛮手中,就会肯定 T 。

法3

加一个特判。

期望得分: \(100pts\)

    if (n % 2 == 0 && m % 2 == 1) {
cout << 0;
return 0;
}

法4

既然这题的正解是 dp,那么我们还是要讲讲 dp 的。

其实 dp 和记忆化没有很大的区别。

状态表示:\(\rm f[i][j]\) 表示第 \(i\) 次传球后球在第 \(j\) 个小朋友手上回到小蛮手中的方案数。

我们发现 \(\rm f[i][j]\) 跟 \(\rm a[x][step]\) 是很像的。

状态转移:\(\rm f[i][j] =
\begin{cases}
\rm f[i - 1][j - 1] & \text{第 i 次传球从左边传给 j}\\
\rm f[i - 1][j + 1] & \text{第 i 次传球从右边传给 j}
\end{cases}\)

这样写对不对?不对!

因为这是环状的,环状的解决方法通常是 \(\mod n\)

\((x + n - 1) \mod n + 1\)

所以正确状态转移为:\(\rm f[i][j] =
\begin{cases}
\rm f[i - 1][(j - 1 + n - 1) \mod n + 1] & \text{第 i 次传球从左边传给 j}\\
\rm f[i - 1][(j + 1 + n - 1) \mod n + 1] & \text{第 i 次传球从右边传给 j}
\end{cases}\)

所以:\(\rm f[i][j] = f[i - 1][(j - 1 + n - 1) \mod n + 1] + f[i - 1][(j + 1 + n - 1) \mod n + 1]\)

做完,最后放上 AC 代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#define line cout << endl
using namespace std;
int f[35][35];
int n, m;
int main() {
cin >> n >> m;
f[1][n] = f[1][2] = 1;
for (int i = 2; i <= m; i++) {
for (int j = 1; j <= n; j++) {
f[i][j] = f[i - 1][(j - 1 + n - 1) % n + 1] + f[i - 1][(j + 1 + n - 1) % n + 1];
}
}
cout << f[m][1] << endl;
return 0;
}

最新文章

  1. mysql中更新或者删除语句中子语句不能操作同一个表You can&#39;t specify target table &#39;test&#39; for update in FROM clause
  2. Web大文件上传控件-jsp-sql示例更新-Xproer.HttpUploader6.2
  3. SharePoint 2013 日历重叠功能简介
  4. (原创)大数据时代:基于微软案例数据库数据挖掘知识点总结(Microsoft 决策树分析算法)
  5. 浅谈输入输出”重定向“——基于Linux系统
  6. Web Performance Test: 如果使用Plugin过滤Dependent Request
  7. Sharepoint 2013 关于&quot;SPChange&quot;简介
  8. iOS开发之网络编程--2、NSURLSessionDownloadTask文件下载
  9. 使用#pragma阻止一些warnings
  10. 多线程程序设计学习(4)guarded suspension模式
  11. 实用的eclipse adt 快捷键
  12. DevExpress MessageBox 弹出框 底层类
  13. AsMVC:一个简单的MVC框架的Java实现
  14. MySQL 二进制日志(Binary Log)
  15. 用U盘和iso镜像文件重装系统
  16. [android]APP启动界面——SplashActivity
  17. Java comparable 和 comparator
  18. php字符串压缩
  19. 蓝桥网试题 java 入门训练 Fibonacci数列
  20. 网站防止SQL注入方法

热门文章

  1. 从头学起Verilog(二):时序逻辑基础与回顾
  2. Java并发编程 - Runnbale、Future、Callable 你不知道的那点事(一)
  3. FTP漏洞利用复现
  4. C# 9 record 并非简单属性 POCO 的语法糖
  5. 思维导图MindManager的过滤主题功能如何使用
  6. MathType中怎么编辑韩文字符
  7. css3系列之animation实现逐帧动画
  8. 对于this和当前线程的一些理解
  9. SRX_Test_2_sound
  10. 【mq读书笔记】消息到达唤醒挂起线程检查新消息