1 条题解
-
0
自动搬运
来自洛谷,原作者为

long_int
**搬运于
2025-08-24 21:33:15,当前版本为作者最后更新于2021-09-05 11:03:52,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
主要思想:动态规划
这一题可以分为两种情况讨论:没有红色块相连接和有连续两个红色块连接。首先要明确:不管是哪种情况,跳棋一定是走奇数的格子,因此最后的计数肯定要在偶数的格子上累加。
- 没有相连的红色块
先看一组样例:
5 0 0 0 1 0不难发现,只需要在偶数上放跳棋,最终就能完成任务。由于题目要求游戏前尽可能放得少,而游戏开始后又只能放在红点上,所以所有的红点都给游戏开始后放。
总结:对于这种情况,第一行输出下标为偶数的点中 0 的个数,第二行输出下标为偶数的店中 1 的个数。
这一步的代码:
bool flag = false; scanf("%lld", &n); for (LL i = 1; i <= n; i++) { scanf("%lld", &q[i]); if (i == 1) { q[i] = 0; //起点的红点没有用,故赋值为 0,作为白点处理 continue; } if (i % 2 == 0) num[q[i]]++; if (q[i] && q[i - 1]) flag = true; } printf("%lld\n%lld", num[0], num[1]);- 有连续至少两块红色块连接
再看一组样例:
8 0 0 0 0 0 1 1 0此时,我们在游戏开始之前不需要放任何棋子。
具体步骤(推出状态转移方程的具体过程):
- 首先在 6 和 7 的位置放棋。
- 然后 7 位置的棋子跳到 5 ,此时 6 被吃掉。因此,在位置 5 的地方得到一个跳棋需要放两次棋,我们记作
dp[5] = 2。 - 这时我们在 6 放置一个棋子,使它吃掉 5 跳到 4,记作
dp[4] = 3。 - 此时若还想向左跳,只需要在 5 上放一个棋子。可是问题来了,5 不是红点,放不了啊!但是我们已经知道 的值是 ,所以 用 2 步使得 5 上面有一个棋子,再跳到 3 即可,记作
dp[3] = 5。 - 重复以上操作,直到 2 处有一个棋子,使得需要到达终点目标棋子跳到 3 的位置。在 4 处得到一个棋子,将目标棋子调到 5 ,一直到终点为止。
通过上述步骤,不难发现一个规律:
想要在 的位置得到一个棋子,必须在 和 处分别放一个棋子,此时需要耗费 的代价。
综上所述,状态转移方程可以很轻松地得到:
- 同理,我们有:
此时代码就很容易写出来了:
for (int i = 3; i <= n; i++) if (q[i] && q[i - 1]) { for (int j = i - 2; j >= 1; j--) dp[j] = min(dp[j], dp[j + 1] + dp[j + 2]); //跳棋向左边跳 for (int j = i + 1; j <= n; j++) dp[j] = min(dp[j], dp[j - 1] + dp[j - 2]); //跳棋向右边跳 }完整的 AC Code:
#include <stdio.h> #include <stdlib.h> #pragma warning(disable : 4996) #define maxn 1005 #define INF 1e18 //这里的INF一定要开够大小,我之前一直开到1e9,只能得60分 #define LL long long #define bool int #define true 1 #define false 0 //由于C语言里没有 bool 型,需要自己宏定义 LL n, q[maxn], num[2]; LL dp[maxn]; LL min(LL a, LL b) { return a < b ? a : b; } void print(bool flag) { //输出函数 if (!flag) { printf("%lld\n%lld", num[0], num[1]); exit(0); //直接结束程序 } LL ans = 0; for (int i = 1; i <= n; i++) ans += (i % 2) ? 0 : dp[i]; //只有偶数点才计入答案 printf("%d\n%lld", 0, ans); } int main() { bool flag = false; scanf("%lld", &n); for (LL i = 1; i <= n; i++) { scanf("%lld", &q[i]); if (i == 1) { q[i] = 0; //起点的红点没有用,故赋值为 0,作为白点处理 continue; } if (i % 2 == 0) num[q[i]]++; if (q[i] && q[i - 1]) flag = true; } for (int i = 1; i <= n; i++) dp[i] = q[i] ? 1 : INF; for (int i = 3; i <= n; i++) if (q[i] && q[i - 1]) { for (int j = i - 2; j >= 1; j--) dp[j] = min(dp[j], dp[j + 1] + dp[j + 2]); //跳棋向左边跳 for (int j = i + 1; j <= n; j++) dp[j] = min(dp[j], dp[j - 1] + dp[j - 2]); //跳棋向右边跳 } print(flag); return 0; }目前也是最优解(话说第二优似乎是抄这篇题解的):

最后提醒:这一题会卡 ,一定要注意!
- 1
信息
- ID
- 1004
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 4
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者