1. 题目

T1 石头剪刀布

题目描述

题目描述

众所周知,石头剪刀布是一项相当考验智商的高级双人游戏。每轮游戏双方可以选择出石头、剪刀或布,胜负关系如下表给出:

玩家 A 玩家 B 胜者
石头 剪刀 A
石头 B
剪刀 A
石头 石头
剪刀 剪刀

Alice 和 Bob 在玩石头剪刀布,但他们觉得这还不够高级,于是决定改进这个游戏。他们准备了 \(10\) 张卡片,其中石头、剪刀、布各 \(3\) 张,以及空白卡片 \(1\) 张。游戏之前先将这 \(10\) 张卡片打乱顺序,平均分成两份,Alice 和 Bob 各自拿 \(5\) 张卡片,通过这些卡片可以玩5轮游戏。每轮游戏双方各自同时出掉一张卡片,游戏规则与上表一致。如果是空白卡片,在出卡之前玩家可以选择在该卡片上画石头、剪刀或者布。每轮游戏中双方出掉的卡会被视为作废,之后不可再使用。

Alice 现在已知自己手上的 \(5\) 张卡片,她想知道在运气最好的情况下,\(5\) 轮游戏过后她最多能够赢 Bob 多少轮游戏。注意,只算赢的轮数,平局不算在内。

输入格式
输入包括一行,\(5\) 个整数。每个整数表示 Alice 手中的卡片状态,其中 \(0\) 表示空白卡,\(1\) 表示石头,\(2\) 表示剪刀,\(3\) 表示布。

输出格式
输出包括一行,\(1\) 个整数。表示 Alice 最多能够赢的游戏轮数。

输入样例 \(1\)

1 1 1 3 3

输出样例 \(1\)

4

输入输出样例 \(1\) 说明
Alice 手上有 \(3\) 张石头,\(2\) 张布。这意味着 Bob 手上有 \(1\) 张布,\(3\) 张剪刀和 \(1\) 张空白卡。那么若游戏过程为下表所示:

轮次 Alice Bob
1 石头 剪刀
2 石头 剪刀
3 石头 剪刀
4 空白(画石头)
5

这样一来,Alice 最多可以赢 \(4\) 局。

输入样例 \(2\)

1 1 2 3 0

输出样例 \(2\)

5

数据规模和约定
数据保证都是合法的,输入的整数都在 \(0\)\(3\) 的范围内。

Sol

直接贪心即可

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<map>
using namespace std;
int cn0,cn1,cn2,cn3,dn0,dn1,dn2,dn3,tmp,ans;
int main()
{
	for (int i=0;i<5;i++)
	{
		scanf("%d",&tmp);
		if (tmp==0) ++cn0;
		else if (tmp==1) ++cn1;
		else if (tmp==2) ++cn2;
		else if (tmp==3) ++cn3;
	} dn0=1-cn0; dn1=3-cn1; dn2=3-cn2; dn3=3-cn3;
	if (cn1<=dn2) dn2-=cn1,ans+=cn1,cn1=0;
	else cn1-=dn2,ans+=dn2,dn2=0;
	if (cn2<=dn3) dn3-=cn2,ans+=cn2,cn2=0;
	else cn2-=dn3,ans+=dn3,dn3=0;
	if (cn3<=dn1) dn1-=cn3,ans+=cn3,cn3=0;
	else cn3-=dn1,ans+=dn1,dn1=0;
	if (dn0&&(cn1||cn2||cn3)) ++ans;
	if (cn0&&(dn1||dn2||dn3)) ++ans;
	printf("%d",ans);
	return 0;
}

T2 铺地毯

题目描述

题目描述

Alice 和她的小伙伴们在家呆着无聊,于是他们发明了一个居家小游戏——铺地毯。

首先,他们在家找到了一块正方形空地,空地可以平均分成 \(N\)\(N\) 列的方格,其中 \(N\) 为奇数,同时从上到下、从左到右标记行的编号从 \(1\)\(N\),列的编号从 \(1\)\(N\)。初始状态下,在空地正中央的格子上放置一只木马。小伙伴们派出 \(K\) 个人参与游戏。游戏持续 \(K\times M\) 轮,每个玩家按照编号顺序轮流进行游戏,因此每人恰好各玩了 \(M\) 轮游戏。游戏开始之前每个人手上都各有 \(M\) 条地毯,不同的玩家手上的地毯颜色是不同的,但同一个玩家的地毯颜色是相同的,地毯的大小是 \(1\times2\),即能够恰好覆盖空地上的 \(2\) 个相邻方格。

\(K\) 个玩家轮流进行游戏。轮到的玩家首先可以选择令木马朝向上、下、左、右其中一个方向,根据计算机所生成一个的随机数前进一定的步数。若在前进的过程中,木马超出空地的范围,则会直接将木马掉头,按照与原本朝向相反的方向前进剩下的步数。若之后再次超出空地范围,则继续将木马掉头并前进,直到步数用完。注意,将木马掉头也算作一步。

例如,假设 \(N=5\),在第 \(1\) 轮游戏中,木马一开始在中心位置 \(S\) 处。若 \(1\) 号玩家获得的随机数为 \(5\),且选择的前进方向是向上。那么,木马先向上前进 \(2\) 步,发现到达边界,于是掉头之后继续再前进 \(3\) 步,到达原本的位置,图中记为 \(T\)

移动完木马后,玩家需要根据木马当前所在位置计算积分。若当前位置未铺设地毯或者地毯颜色为自己的颜色,则不需要付钱。若当前位置铺设了另一位玩家的地毯,则需要按照当前空地上该颜色地毯所覆盖的格子总数支付给对方租金,每个方格按照 \(1\) 元金币计算。

随后,玩家会选择在空地上的某个位置铺上一条自己颜色的地毯,地毯会覆盖 \(2\) 个相邻的方格。若该位置本来已有地毯,则新铺的地毯会完全覆盖掉旧的地毯。即:计算积分时仅以最上层的地毯颜色为准。

游戏会持续 \(K\times M\) 轮,每位玩家轮流进行,直到所有玩家的地毯使用完。游戏开始之前,每位玩家持有足够多的金币,不存在中途付不起租金的情况。现给出每一轮游戏中木马的移动方式和铺设地毯的位置,请你计算游戏结束后每位玩家各赚了多少钱,如果是亏损的状态,则输出负数即可。

输入格式

输入包括 \(KM+1\) 行。

第一行为 \(3\) 个整数,分别为 \(N,K,M\)。表示空地大小为 \(N\)\(N\) 列,有 \(K\) 个玩家参与游戏,每个玩家初始状态下有 \(M\) 条地毯。

\(2\)\(KM+1\) 行,每行包含 \(5\) 个整数,依次为 \(a,b,op,x,y\)。表示当前轮到的玩家选择令木马朝 \(a\) 方向前进 \(b\) 步。其中 \(a\)0123 时分别表示朝上、下、左、右。\(op,x,y\) 描述了该名玩家放置地毯的方式。\(op\) 描述了地毯的方向,\(op\)\(0\) 时,地毯为横向放置,即覆盖了第 \(x\) 行第 \(y\) 列和第 \(x\) 行第 \(y+1\) 列的格子。\(op\)\(1\) 时,地毯为纵向放置,即覆盖了第 \(x\) 行第 \(y\) 列和第 \(x+1\) 行第 \(y\) 列的格子。

输出格式

输出包括一行,为 \(K\) 个整数,表示游戏结束后每个玩家所赚的钱数。

输入样例 \(1\)

3 2 1
0 2 0 1 1
2 2 1 1 1

输出样例 \(1\)

2 -2

输入输出样例 \(1\) 说明

空地大小为 \(3\times 3\),有 \(2\) 个玩家参与游戏,初始状态下每个玩家各有 \(1\) 条地毯。

初始状态下,木马放置于第 \(2\) 行第 \(2\) 列的方格内。

第一轮,\(1\) 号玩家将木马向上移动 \(2\) 格,超出空地后掉头,木马停留在了第 \(1\) 行第 \(2\) 列。此时,空地上未有地毯,因此玩家不需要支付租金。随后,玩家在第 \(1\) 行第 \(1\) 列、第 \(1\) 行第 \(2\) 列的方格铺上地毯。

第二轮,\(2\) 号玩家将木马向左移动 \(2\) 格,超出空地后掉头,木马停留在了第 \(1\) 行第 \(1\) 列。此时,该位置上铺了 \(1\) 号玩家的地毯,因此 \(2\) 号玩家需要支付租金。由于当前空地上铺了 \(1\) 号玩家地毯的格子数为 \(2\),因此 \(1\) 号玩家需要支付给 \(2\) 号玩家 \(2\) 个金币。随后,玩家在第 \(1\) 行第 \(1\) 列、第 \(2\) 行第 \(1\) 列的方格铺上自己颜色的地毯。

输入样例 \(2\)

5 4 3
0 4 0 2 1
3 7 1 3 2
1 3 0 4 2
3 7 1 1 3
0 3 0 2 3 
3 2 1 2 4
2 4 0 4 1
1 3 1 4 4
3 3 0 2 4
0 3 1 3 3
2 2 0 3 2
1 2 1 4 3

输出样例 \(2\)

10 -7 -4 1

输入输出样例 \(2\) 说明

空地大小为 \(5\times5\),有 \(4\) 个玩家参与游戏,初始状态下每个玩家各有 \(3\) 条地毯。假定四个玩家的地毯颜色分别为红、蓝、绿、黄。游戏持续了 \(12\) 轮,每一轮玩家操作完成后的状态如下图所示:

\(1\) 号玩家在第 \(5\) 轮游戏中给了 \(4\) 号玩家 \(2\) 个金币,在第 \(9\) 轮游戏中给了 \(4\) 号玩家 \(3\) 个金币。
\(2\) 号玩家在第 \(2\) 轮游戏中给了 \(1\) 号玩家 \(2\) 个金币,在第 \(10\) 轮游戏中给了 \(1\) 号玩家 \(5\) 个金币。
\(3\) 号玩家在第 \(7\) 轮游戏中给了 \(1\) 号玩家 \(3\) 个金币,在第 \(11\) 轮游戏中给了 \(1\) 号玩家 \(5\) 个金币。
\(4\) 号玩家在第 \(12\) 轮游戏中给了 \(3\) 号玩家 \(4\) 个金币。

数据规模和约定

测试点编号 \(N\) \(K\) \(M\) \(B\)
\(1\) \(3\) \(2\) \(\le 5\) \(\le 10\)
\(2\) \(3\) \(5\) \(\le 10\) \(\le 10\)
\(3\sim 5\) \(\le 10\) \(\le 100\) \(\le 100\) \(\le 100\)
\(6\sim 10\) \(\le 50\) \(\le 200\) \(\le 200\) \(\le 200\)
\(11\sim 15\) \(\le 100\) \(\le 500\) \(\le 500\) \(\le 500\)
\(16\sim 20\) \(\le 1000\) \(\le 1000\) \(\le 1000\) \(\le 2^{31}-1\)

对于所有的测试点,\(1≤N≤1000\)\(1≤K≤1,000\)\(1≤M≤1,000\)\(0≤a≤3\)\(0≤b≤2^{31}-1\)

数据保证都是合法的,且数据随机生成。

Sol

模拟,注意用桶统计数量并且用取模优化行走。

T3 数列游戏

题目描述

题目描述

Alice 最近在数学课上学习了互质。若两个整数的公约数只有 \(1\),则认为这两个数互质。例如,\(2\)\(5\) 是互质的,它们的公约数只有 \(1\)。而 \(12\)\(36\) 显然不是互质的,它们的公约数不只有 \(1\),还有 \(2,3,4,6,12\)

为了帮助 Alice 巩固互质的知识,Bob 给她出了一个难题。Bob 给出一个长度为 \(n\) 的数列,数列里的每个数都是 \(100000\) 以内的正整数,且不为 \(1\)。他让 Alice 写出长度同样为 \(n\) 的数列,同时要求这个数列满足:

  1. 字典序大于等于 Bob 给出的数列。
  2. 数列中的任意两个数必须是互质的。也就是说,在数列中无论选择哪两个数,都满足互质的条件。
  3. 该数列是满足前两个条件的所有数列中字典序最小的。

比较数列 \(a_1,a_2,a_3,…,a_n\) 和数列 \(b_1,b_2,b_3,…,b_n\) 字典序的方法为:从数列的第 \(1\) 项开始比较,若 \(a_1>b_1\),则认为数列 \(a\) 字典序大于数列 \(b\);若 \(a_1<b_1\),则认为数列 \(a\) 字典序小于数列 \(b\);若 \(a_1=b_1\),则比较下一项,如此类推。若直到数列到达最后一项都未有比较结果,则数列 \(a\) 和数列 \(b\) 完全相同,二者的字典序是相等的。

输入格式

输入包括两行。
第一行为 \(1\) 个整数,表示数列的长度 \(N\)
第二行为 \(N\) 个整数,表示 Bob 给出的数列。

输出格式

输出包括一行,为 \(N\) 个整数,表示 Alice 给出的数列。

输入样例 \(1\)

3
2 2 2

输出样例 \(1\)

2 3 5

输入输出样例 \(1\) 说明

要求生成字典序大于等于 \(2,2,2\) 且两两互质的字典序最小数列,显然只能是 \(2,3,5\)

输入样例 \(2\)

5
10 3 7 2 5

输出样例 \(2\)

10 3 7 11 13

数据规模和约定

测试点编号 \(N\) 数列中每一项
\(1\) \(3\) \(\le 10\)
\(2\) \(5\) \(\le 20\)
\(3\sim 5\) \(\le 10\) \(\le 100\)
\(6\sim 10\) \(\le 100\) \(\le 100\)
\(11\sim 15\) \(\le 1000\) \(\le 1000\)
\(16\sim 20\) \(\le 10^5\) \(\le 10^5\)

对于所有测试点,\(1≤N≤10^5\)
数列中每一项都在 \(2\)\(10^5\) 范围内。

Sol

显然如果对于一个位置 \(i\),Alice 填的数严格大于 Bob 填的数时,后面就可以随便填了,因为字典序保证了。

填的时候注意筛下素因子。

T4 数星星

题目描述

题目描述

Alice 最近在沉迷玩一个游戏,名字叫数星星。游戏中给出一幅布满星星的地图,地图可以平均分成 \(N\)\(M\) 列的方格,每个方格中可能有若干颗星星,也可能没有星星。对于地图上的每一行,你可以用一个长度随意调节的魔法框,框选出同一行中连续的若干个方格。你可以获得被框住的方格所包含的全部星星。在地图上的每一行,你最多只能使用一次魔法框,你也可以选择不使用。同时,使用魔法框需要花钱,所花的金币数为当前所使用的魔法框的长度。每次使用时,魔法框可以重新调节长度。

现在你的任务是至少获取 \(K\) 颗星星,问最少需要花费多少金币。

输入格式

输入包含 \(N+1\)行。

第一行为 \(3\) 个整数 \(N,M,K\),表示图的大小是 \(N\)\(M\) 列。任务是至少需要获取 \(K\) 颗星星。

接下来为 \(N\) 行,每行包括 \(M\) 个非负整数,表示该方格所包含的星星的数量。

输出格式

输出包含 \(1\) 行,为 \(1\) 个整数,表示最少需要花费的金币数。

输入样例 \(1\)

3 3 6
1 0 1
1 1 1
1 0 1

输出样例 \(1\)

7

输入输出样例 \(1\) 说明

其中一种最优方案是:第一行从 \(1\) 框到 \(3\),第二行从 \(1\) 框到 \(3\),第三行只框 \(1\) 或者只框 \(3\)。因此总长度是 \(7\),共花费 \(7\) 个金币。

输入样例 \(2\)

5 5 54
0 0 0 0 6 
0 0 0 0 6 
0 0 1 6 6 
0 0 3 10 0 
9 5 0 0 8

输出样例 \(2\)

10

数据规模和约定

测试点编号 \(N\) \(M\) \(K\)
\(1\) \(2\) \(2\) \(\le 50\)
\(2\) \(3\) \(3\) \(\le 100\)
\(3\sim 5\) \(\le 5\) \(\le 5\) \(\le 200\)
\(6\sim 10\) \(\le 20\) \(\le 20\) \(\le 10000\)
\(11\sim 15\) \(\le 50\) \(\le 50\) \(\le 10^6\)
\(16\sim 20\) \(\le 100\) \(\le 100\) \(\le 2^{31}-1\)

对于所有测试点,保证:\(1≤N\times M≤5000\)\(1≤K≤2^{31}–1\)
每个方格内包含的星星数量都在 \(0\)\(10000\) 之间。

保证答案有解。

Sol

初步状态:因为 \(k\) 很大,考虑反着定义状态。
\(dp_i\) 为使用 \(i\) 个金币获得的星星数的最大值。
那么令 \(dp_i\ge k\) 的最小的 \(i\) 就是答案。

因为有行数限制,所以令 \(dp_{i,j}\) 为到第 \(i\) 行,使用 \(j\) 个金币获得的星星数的最大值。
若第 \(i\) 行选了 \(p\sim q\),则目前为 \(dp_{i-1,j-(q-p+1)}+\operatorname{sum}(p,q)\) .

考虑暴力算第 \(i\) 行,枚举 \(p,q\)\(p : 1\sim M\,;\,q : p\sim M\))即可。
还可能不选,就为 \(dp_{i-1,j}\) .
把上面所有东西取个 \(\max\) 即可,因为状态是 \(O(m^2)\) 的,转移是 \(O(1)\) 的,枚举 \(p,q\)\(O(m^2)\) 的,总复杂度是 \(O(m^4)\) 的。

注意到我们关注的只是长度,和 \(p,q\) 没什么关系,考虑预处理。
维护个 \(g_{i,j}\) 为第 \(i\) 行长度为 \(j\) 的最大连续和,三重循环预处理即可。

然后转移的时候枚举个长度即可。

2. 算法 -- 动态规划

1. 概述

思想:

Those who cannot remember the past are condemned to repeat it.

常见问题

  1. 做决策
  2. 求最优解
  3. 求方案数
  4. 求可行解

一般是搜索不能用,贪心不敢用的题。

步骤

  1. 设计状态(阶段):
    1. 问什么设什么
    2. 从数据范围里猜测状态
    3. 两种思路
      1. 满状态求出的最优解就是答案(最优解问题)
      2. 状态就是答案,该状态是否能够达到即为答案是否合法(可行解问题)
    4. 大问题 \(\to\) 小问题
    5. 由状态定义分类为 线性 dp、区间 dp、背包 dp、树形 dp、状压 dp、\(\cdots\cdots\)
  2. 设计初始状态
    1. 既不需要太多决策,能够立刻得到答案的最小问题
    2. 常见:
      1. 线性:左右端点,边界外
      2. 区间:长度为 \(0/1/2\cdots\)
      3. 分配/背包:背包大小为 \(0\)
      4. 树形:叶子节点,根节点
      5. 状压:\(0\) 状态或起始状态
    3. 作为状态转移的已知条件
  3. 推导状态转移方程
    1. 思考如何从小问题推导成大问题
    2. 通常需要分情况讨论:考虑最后一步是如何做决策的
    3. 由状态转移方程来决定推导顺序
      1. 线性:从左到右 / 从右到左
      2. 区间:从短到长
      3. 分配/背包:从小背包推到大背包
      4. 树形:自底向上,自上而下
      5. 状压:从 \(0\) 到满,从起始到目标
      6. 难以确定:记忆化搜索

2. 线性 dp

Problem 1:最长不下降子序列(LIS)

有一个长为 \(n\) 的数列 \(a_1,a_2,\cdots,a_n\),请求出这个序列中最长不下降子序列的长度。

不下降序列指的是对于任意的 \(i<j\) 都满足 \(a_i\le a_j\) 的子序列。
e.g. \(1,3,4,7,8,9,15,10^{233}\) 就是一个不下降序列;\(1,1,1,2,5,10^5,10^{10^5}\) 也是一个不下降序列。

状态:\(dp_i\) 表示以第 \(i\) 个数结尾的 LIS 长度。
转移:\(dp_i=\max\{dp_j+1\}\),其中 \(1\le j<i\)\(a[j]\le a[i]\) .
初始状态:\(dp_0=0\) , \(a_0=-\infty\)

时间复杂度 \(O(n^2)\) .


Problem 2:花匠(NOIp2013;洛谷 P1970;loj 2612

花匠栋栋种了一排花,每株花都有自己的高度。花儿越长越大,也越来越挤。栋栋决定把这排中的一部分花移走,将剩下的留在原地,使得剩下的花能有空间长大,同时,栋栋希望剩下的花排列得比较别致。

具体而言,栋栋的花的高度可以看成一列整数 \(h_1,h_2,\cdots,h_n\)。设当一部分花被移走后,剩下的花的高度依次为 \(g_1,g_2,\cdots,g_m\),则栋栋希望下面两个条件中至少有一个满足:

  • 对于所有的整数 \(i\)\(g_{2i}>g_{2i−1}\),且 \(g_{2i}>g_{2i+1}\)
  • 对于所有的整数 \(i\)\(g_{2i}<g_{2i−1}\),且 \(g_{2i}<g_{2i+1}\)

注意上面两个条件在 \(m=1\) 时同时满足,当 \(m>1\) 时最多有一个能满足。
请问,栋栋最多能将多少株花留在原地。

\(dp_{i,0}\) 表示现在在第 \(i\) 盆花且第 \(i\) 盆花在「凹」的位置上的方案数,\(dp_{i,1}\) 表示现在在第 \(i\) 盆花且第 \(i\) 盆花在「凸」的位置上的方案数。

分类讨论:

  1. \(h_{i-1}\le h_i\)
    • \(dp_{i,1}=dp_{i-1,0}+1\)
    • \(dp_{i,0}=dp_{i-1,0}\)(移走前面那盆)
  2. \(h[i-1]>h[i]\)
    • \(dp_{i,0}=dp_{i-1,1}+1\)
    • \(dp_{i,1}=dp_{i-1,1}\)(同)

Problem 3:摆渡车(NOIp2018;洛谷 P5017;loj 3001

\(n\) 名同学要乘坐摆渡车从人大附中前往人民大学,第 \(i\) 位同学在第 \(t_i\) 分钟去等车。只有一辆摆渡车在工作,但摆渡车容量可以视为无限大。摆渡车从人大附中出发、把车上的同学送到人民大学、再回到人大附中(去接其他同学),这样往返一趟总共花费 \(m\) 分钟(同学上下车时间忽略不计)。摆渡车要将所有同学都送到人民大学。

凯凯很好奇,如果他能任意安排摆渡车出发的时间,那么这些同学的等车时间之和最小为多少呢?

注意:摆渡车回到人大附中后可以即刻出发。

\(dp_{i,T}\) 为现在为第 \(i\) 个同学,开了 \(T\) 时间车的最少等待总时间。
显然发车时间为 \(T-M\),若 \(T-M-t_i\ge M\),那么让这个人发车后在上去就会更优,所以 \(T\) 的范围不会超过 \(2M\)
则枚举一个 \(j\)\(j\sim i\) 的同学一起上做转移即可。
转移:\(dp_{i,t''+m}=\min\{dp_{j-1,t}+\operatorname{wait}(j,i)\}\)


Problem 3.5:飞扬的小鸟(NOIp2014;洛谷 P1941;loj 2500

Flappy Bird 是一款风靡一时的休闲手机游戏。玩家需要不断控制点击手机屏幕的频率来调节小鸟的飞行高度,让小鸟顺利通过画面右方的管道缝隙。如果小鸟一不小心撞到了水管或者掉在地上的话,便宣告失败。
为了简化问题,我们对游戏规则进行了简化和改编:

  1. 游戏界面是一个长为 \(n\) ,高为 \(m\) 的二维平面,其中有 \(k\) 个管道(忽略管道的宽度)。
  2. 小鸟始终在游戏界面内移动。小鸟从游戏界面最左边任意整数高度位置出发,到达游戏界面最右边时,游戏完成。
  3. 小鸟每个单位时间沿横坐标方向右移的距离为 \(1\) ,竖直移动的距离由玩家控制。如果点击屏幕,小鸟就会上升一定高度 \(X\),每个单位时间可以点击多次,效果叠加;如果不点击屏幕,小鸟就会下降一定高度 \(Y\) 。小鸟位于横坐标方向不同位置时,上升的高度 \(X\) 和下降的高度 \(Y\) 可能互不相同。
  4. 小鸟高度等于 \(0\) 或者小鸟碰到管道时,游戏失败。小鸟高度为 \(m\) 时,无法再上升。

现在,请你判断是否可以完成游戏。如果可以,输出最少点击屏幕数;否则,输出小鸟最多可以通过多少个管道缝隙。

\(dp_{i,j}\) 为走到 \((i,j)\) 的最少步数。

对于一个向上跳的操作,如果它下方的位置可以被跳到,那么它一定可以由它下方的区间跳到。

转移:

  1. 向下移动 \(dp_{i+1,j}=\min(dp_{i+1,j},dp_{i,j})\) .
  2. 向上移动 \(dp_{i+1,j}=\min(dp_{i+1,j},dp_{i,j-X_i}+1)\) .
  3. 对于可以由它下方节点向上跳节点得到的位置 \(dp_{i+1,j}=\min(dp_{i+1,j},dp_{i+1,j-X_i}+1)\) .

对于所有点都进行 dp 即可。
注意细节。

3. 区间 dp

Problem 4:石子合并(NOI1995;洛谷 P1880;loj 10147

\(n\) 堆石子绕圆形操场排放,现要将石子有序地合并成一堆。规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数记做该次合并的得分。

请编写一个程序,读入堆数 \(n\) 及每堆的石子数,并进行如下计算:

  • 选择一种合并石子的方案,使得做 \(n-1\) 次合并得分总和最大。
  • 选择一种合并石子的方案,使得做 \(n-1\) 次合并得分总和最小。

当然先破环为链,就变成链上问题了。

\(dp_{i,j}\) 表示将 \(i\sim j\) 合并为一堆所需要的最小代价。
枚举一个 \(k\),使得 \(i\sim j\)\(i\sim k\)\(k+1\sim j\) 合并而来。
即转移为 \(dp_{i,j}=\min\{dp_{i,k}+dp_{k+1,j}+\operatorname{sum}(i,j)\}\)

因为长度大的要用长度小的推,所以长度要从小到大推,也就是先枚举起点 \(i\),然后根据长度推终点 \(j\)

最大同理。


Problem 5:凸多边形三角剖分(loj 10149

给定一个有 \(n\) 个顶点的凸多边形,其中每个点都有一个权值,问如何将这个凸多边形分成 \(n-2\) 个三角形,使得这些三角形的顶点权值乘积之和最小?
\(n\le 50\)

提一下,众所周知,凸多边形三角剖分方案数是 \(C_n\)(其中 \(C\) 为 Catalan 数)

显然连一个三角形就只需要将这三角形左右的凸多边形划分就好了,如图:

把它倒着推就成合并了,直接区间 dp 即可。
\(dp_{i,j}\) 表示 \(v_i\sim v_j\)(v 表示这个多边形的点,\(w\) 表示点权)合并为一堆所需要的最小代价。

转移为:\(dp_{i,j}=\min\{dp_{i,k}+dp_{k,j}+w_iw_jw_k\}\) .


Problem 6:子串(NOIp2015;洛谷 P2679;loj 2424

有两个仅包含小写英文字母的字符串 \(A\)\(B\)。现在要从字符串 中取出 \(k\) 个互不重叠的非空子串,然后把这 \(k\) 个子串按照其在字符串 中出现的顺序依次连接起来得到一个新的字符串,请问有多少种方案可以使得这个新串与字符串 \(B\) 相等?

注意:子串取出的位置不同也认为是不同的方案。

\(dp_{i,j,k}\) 为现在 \(A\) 匹配到了第 \(i\) 位,\(B\) 匹配到了第 \(j\) 位,当前取了 \(k\) 个子串的方案数。

考虑第 \(k\) 个子串(最后一个)是怎么选取的:

  1. 选到了第 i 位
  2. 没选到第 i 位

因为状态已经 \(O(n^3)\) 了,所以转移必须 \(O(1)\)

\(F_{i,j,k}\) 表示现在 \(A\) 匹配到了第 \(i\) 位,\(B\) 匹配到了第 \(j\) 位,当前取了 \(k\) 个子串并且第 \(i\) 个位置选的数目;\(G_{i,j,k}\) 表示现在 \(A\) 匹配到了第 \(i\) 位,\(B\) 匹配到了第 \(j\) 位,当前取了 \(k\) 个子串并且第 \(i\) 个位置不选的数目;

\(F_{i,j,k}\) 的转移:

  1. \(A_i=B_j\)
    1. 跟着 \(i-1\) 子串:\(F_{i-1,j-1,k}\)
    2. 新开一个子串:\(F_{i-1,j-1,k-1}+G_{i-1,j-1,k-1}\)
  2. \(A_i\neq B_j\),选不了,此时 \(F_{i,j,k}=0\) .

两个求和即可。

\(G_{i,j,k}\) 的转移:\(F_{i-1,j,k}+G_{i-1,j,k}\) .

三维会 MLE,因为 \(F/G_{i\,,\,\cdots\,,\,\cdots}\) 只与 \(F/G_{i-1\,,\,\cdots\,,\,\cdots}\) 相关,考虑滚动数组:

\(i\) 还是同样枚举,令 \(now=i \bmod 2\)\(last=now\oplus 1\),其中 \(\oplus\) 为按位异或运算,在 C++/C/java 语言中为 ^ 运算符,在 pascal 语言中为 xor 运算符,参见 Wikipedia - Bitwise operation - XOR

每次把 \(F,G\) 初始化为 \(0\),把转移里的所有 \(i\) 换成 \(now\),所有 \(i-1\) 换成 \(last\) 即可。

4. 分配 dp(背包)

将有限的资源分给若干使用者,从而获得最大的利益。
简单来说假设资源总数量为 \(m\),分配给 \(n\) 个使用者,给第 \(i\) 个使用者 \(x_i\) 个资源所得到的收益是 \(f_i(x_i)\) .

则问题为:

\[x_1+x_2+\cdots+x_n\le m \]
\[x_i\ge 0 \qquad (i=1,2,\cdots,n) \]

使得 \(f_1(x_1)+\dots+f_n(x_n)\) 最大。


Problem 7:01 背包问题(洛谷 P1048;loj 6559;hdu 2602

\(n\) 个物品,每个物品有一个重量 \(w_i\) 和一个价值 \(c_i\)。有一个背包,背包中最多只能承受 \(W\) 的重量。

将这些物品放入背包中,最多能够获得多少价值?

\(dp_{i,j}\) 表示前 \(i\) 个物品正好放入一个大小为 \(j\) 的背包的最大价值。

显然 \(dp_{i,j}=max(dp_{i-1,j-w_i}+c_i,dp_{i-1,j})\)(选或不选)

因为 \(dp_{i,j}\) 只与 \(dp_{i-1\,,\,\cdots}\) 有关,所以可以滚动数组。
\(dp_j\) 表示背包大小为 \(j\) 的当前最大价值,更新的时候需要用到旧值,要倒序循环防止旧状态被覆盖。


Problem 8:完全背包问题(洛谷 P1616

\(n\) 种物品,每种物品有一个重量 \(w_i\) 和一个价值 \(c_i\),且每种物品都有无穷个。有一个背包,背包中最多只能承受 \(W\) 的重量。

将这些物品放入背包中,最多能够获得多少价值?

直接把 01 背包的滚动数组循环变成正序即可,因为选取的元素都已经可以是无穷个了,所以覆盖了就是多放几个了。


Problem 9:多重背包问题(洛谷 P1776

\(n\) 种物品,每种物品有一个重量 \(w_i\) 和一个价值 \(c_i\),且每种物品有 \(t_i\) 个。有一个背包,背包中最多只能承受 \(W\) 的重量。

将这些物品放入背包中,最多能够获得多少价值?

\(dp_{i,j}\) 表示前 \(i\) 种物品正好放入一个大小为 \(j\) 的背包的最大价值。

枚举一个 \(k\)(放入多少个),转移就和 01 背包差不多了:\(dp_{i,j}=\max\{dp_{i-1,j-k\cdot c_i}+k\cdot v_i,dp_{i-1,j}\}\) .

二进制分组优化 :把 \(k\) 分成多份,每一份包含 \(2^i\) 个,然后做 01 背包即可。


Problem 10:货币系统(NOIp2018;洛谷 P5020;loj 2951

在网友的国度***有 \(n\) 种不同面额的货币,第 \(i\) 种货币的面额为 \(a_i\),你可以假设每一种货币都有无穷多张。为了方便,我们把货币种数为 \(n\)、面额数组为 \(a_{1\dots n}\) 的货币系统记作 \((n,a)\)

在一个完善的货币系统中,每一个非负整数的金额 \(x\) 都应该可以被表示出,即对每一个非负整数 \(x\),都存在 \(n\) 个非负整数 \(t_i\) 满足 \(a_i\cdot t_i\) 的和为 \(x\)。然而,在网友的国度中,货币系统可能是不完善的,即可能存在金额 \(x\) 不能被该货币系统表示出。例如在货币系统 \(n=3\), \(a=[2,5,9]\) 中,金额 \(1,3\) 就无法被表示出来。

两个货币系统 \((n,a)\)\((m,b)\) 是等价的,当且仅当对于任意非负整数 \(x\),它要么均可以被两个货币系统表出,要么不能被其中任何一个表出。

现在网友们打算简化一下货币系统。他们希望找到一个货币系统 \((m,b)\),满足 \((m,b)\) 与原来的货币系统 \((n,a)\) 等价,且 \(m\) 尽可能的小。他们希望你来协助完成这个艰巨的任务:找到最小的 \(m\)

表示数算是个反向分配罢,令 \(dp_j\) 表示 \(j\) 能不能被货币系统表示即可。

dp[0]=true
for i : 1...n
	if dp[a[i]] 第 i 种货币不需要 
	for j : a[i]...25000
		dp[j] = dp[j] or dp[j-a[i]]

Problem 11:纪念品(CSP-J 2019;洛谷 P5662

小伟突然获得了一种超能力,他知道未来 \(T\)\(N\) 种纪念品每天的价格。某个纪念品的价格是指购买一个该纪念品所需的金币数量,以及卖出一个该纪念品换回的金币数量。

每天,小伟可以进行以下两种交易无限次:

  1. 任选一个纪念品,若手上有足够金币,以当日价格购买该纪念品;
  2. 卖出持有的任意一个纪念品,以当日价格换回金币。

每天卖出纪念品换回的金币可以立即用于购买纪念品,当日购买的纪念品也可以当日卖出换回金币。当然,一直持有纪念品也是可以的。

\(T\) 天之后,小伟的超能力消失。因此他一定会在第 \(T\) 天卖出所有纪念品换回金币。
小伟现在有 \(M\) 枚金币,他想要在超能力消失后拥有尽可能多的金币。

因为当日购买的纪念品也可以当日卖出换回金币,所以直接每天先卖后买即可。
每个纪念品买和卖之间相隔多天:在相邻两天完成,即前一天剩下 \(x\) 元,在前一天买了一些纪念品之后,在今天卖掉。
然后考虑每一天买之前剩下的最多钱数即可。

\(P_{i,j}\) 表示第 i 天第 j 种纪念品的价格。

每次第 \(i\) 天进行:

  1. 卖掉纪念品
  2. 以当天价格买纪念品

\(dp_{i,j}\) 为第 \(i\) 天卖完纪念品还没买,手上有 \(j\) 元时最多能拥有的钱数。

枚举纪念品 \(k\),转移就是 \(dp_{i,j}=\max\{dp_{i,j-P_{i,k}}+P_{i+1,k}-P_{i,k}\}\) 了。

但是发现它没有令 \(dp_{i,j}\) 转移为 \(dp[i-\bf CONSTANT\rm ][\dots]\)

枚举 \(j : \cdots10000\),那么在第 \(i\) 天把物品倒一下能赚到的钱就是 \(S=\max\{dp_{i,j}\}\)
那么第 \(i+1\) 天的收益就是 \(dp_{i+1,S}\) 了,反复转移即可。


Problem 12(树上背包):Apple Tree

给定 \(N\) 个点的树,节点处有一定数量的苹果。

\(A\)\(1\) 号点出发在树上走 \(K\) 步,并吃掉经过的节点上的所有苹果(不能重复吃), 求最多能吃到多少苹果。

\(dp_{i,step,1}\) 表示从 \(i\) 号节点出发,走 \(\le step\) 步回到 \(i\) 节点能吃到的最多苹果数,\(dp_{i,step,0}\) 表示从 \(i\) 号节点出发,走 \(\le step\) 步不回到 \(i\) 节点能吃到的最多苹果数。

\(dp_{i,step,1}\) 的转移:

枚举 \(j\)\(i\) 的儿子:\(dp_{j,step-2,1}\) .
枚举一个 \(k\) 为分配给儿子 \(j\) 的步数,则目为 \(dp_{i,step-2-k,1}\) .
总为 \(dp_{j,step-2,1}+dp_{i,step-2-k,1}\),对于所有情况取个 \(\max\) 即可。
其实这两个可以合并,\(k\) 枚举到 \(0\) 时即为第一种情况

\(dp_{i,step,0}\) 的转移:
枚举 \(j\)\(i\) 的儿子:\(dp_{j,step-1,0}\) .
枚举一个 \(k\) 为分配给再也不回来的儿子 \(j\) 的步数,则为 \(dp_{i,step-1-k,1}+dp_{k,0}\)(还得加上回来的儿子)
总为 \(dp_{j,step-1,0}+dp_{i,step-1-k,1}+dp_{k,0}\),对于所有情况取个 \(\max\) 即可。
同,这两个也可以合并。

时间复杂度 \(O(n^2k^2)\) .