当前位置: 首页 > article >正文

洛谷 P2157 [SDOI2009] 学校食堂


题目传送门


前言

第一次见这么新颖的 d p dp dp,竟然可以从当前枚举的 i i i 的前面或者后面转移过来,这不就有后效性了吗?

好了开玩笑

其实只要状态设计好,还是没有后效性的。


思路

状态设计

首先 B i ≤ 7 B_i \leq 7 Bi7,所以肯定是状压 d p dp dp,所以一定起码有两维:一维是当前枚举到的 i i i,一维是压缩后的状态 s s s(具体是什么等会说)。
然后他又说 【当前做的菜所用的时间】 还和 【前一个菜的口味】 有关系,所以需要增加一维,表示上一个打到饭的同学是 j j j
但是这样开的话数组会炸。由于 B i ≤ 7 B_i \leq 7 Bi7,所以我们第三位可以用 j j j 来表示上一个打饭的同学是 i + j i + j i+j j j j 的范围等会再说)。

现在来看 d p i , s , j dp_{i, s, j} dpi,s,j 的含义:表示 [ 1 , i − 1 ] [1, i-1] [1,i1] 的同学 【已经全部打完饭】 ,现在枚举到第 i i i 个同学, 【他以及他身后 7 7 7 个同学】 的打饭状态是 s s s【目前最后一个打饭的同学】 i + j i + j i+j,此时所用的最少时间。

j j j 的取值范围是 [ − 8 , 7 ] [-8, 7] [8,7]。右边界的 7 7 7 很容易明白,就是同学 i i i 最多能忍受到的地方。左边界的 − 8 -8 8 是因为:由于上个打饭的同学 【也是 [ 1 , i − 1 ] [1, i - 1] [1,i1] 同学中最后一个打饭的】,所以第 i − 1 i - 1 i1 个同学打饭时,他最多插到第 i − 1 − 7 = i − 8 i - 1 - 7 = i - 8 i17=i8 个同学的前面,所以左边界是 − 1 -1 1
为了防止数组越界,在代码实现时只需要给下标加上一个偏移量就好。

状态转移

首先要分类:第 i i i 个同学已经打过饭了(即 s   &   1 = = 1 s \ \& \ 1 == 1 s & 1==1),和第 i i i 个同学还没打饭(即 s   &   1 = = 0 s \ \& \ 1 == 0 s & 1==0)。

为什么这么分呢?
因为无论做哪道 d p dp dp 时,我们所要做的状态转移就是 【把第 i i i 位的状态转移到第 i + 1 i + 1 i+1 位】
而在这道题中,只有当 s   &   1 = = 1 s \ \& \ 1 == 1 s & 1==1 时,它才能往下一位转移(解释在下面)。

  1. s   &   1 = = 1 s \ \& \ 1 == 1 s & 1==1 时, d p i , s , j dp_{i, s, j} dpi,s,j 可以转移到 d p i + 1 , s > > 1 , j − 1 dp_{i + 1, s >> 1, j - 1} dpi+1,s>>1,j1。因为 i i i 的时间花费已经被计算过了,我们要考虑的就是第 i + 1 i + 1 i+1 位及以后的学生。而且对于 i i i 来说,目前最后一个打饭同学是 i + j i + j i+j,那么对于 i + 1 i + 1 i+1 来说,目前最后一个打饭的同学是 i + 1 + j − 1 = = i + j i + 1 + j - 1 == i + j i+1+j1==i+j,二者是等价的,因此可以转移。
    转移方程:
    d p i + 1 , s > > 1 , j − 1 = m i n ( d p i + 1 , s > > 1 , j − 1 , d p i , s , j ) dp_{i + 1, s >> 1, j - 1} = min(dp_{i + 1, s >> 1, j - 1}, dp_{i, s, j}) dpi+1,s>>1,j1=min(dpi+1,s>>1,j1,dpi,s,j)
  2. s   &   1 = = 0 s \ \& \ 1 == 0 s & 1==0 时,此时无法再向 i + 1 i + 1 i+1 位转移,因为不满足规定的前 ( i + 1 ) − 1 (i + 1) - 1 (i+1)1 位同学都打完了饭。但是我们可以转移一下第二维的状态 s s s,我们可以找到一个 [ i , i + 7 ] [i, i + 7] [i,i+7] 区间内没打饭的同学,并现在让他打饭来转移。但是需要注意一点,就是转移时看看 【有没有超过某个同学的忍耐范围】,没超过就转移。
    枚举当前状态 s s s 的二进制位 k k k,若 s   &   ( 1 < < k ) = = 0 s \ \& \ (1 << k) == 0 s & (1<<k)==0,那么就有转移方程:
    d p i , s ∣ ( 1 < < k ) , k = m i n ( d p i , s ∣ ( 1 < < k ) , k , d p i , s , j + ( T i + j ⊕ T i + k ) ) dp_{i, s | (1 << k), k} = min(dp_{i, s | (1 << k), k}, dp_{i, s, j} + (T_{i + j} \oplus T_{i + k})) dpi,s(1<<k),k=min(dpi,s(1<<k),k,dpi,s,j+(Ti+jTi+k))
边界条件

d p 1 , 0 , 0 dp_{1, 0, 0} dp1,0,0 设为 0 0 0,其他全设为 I N F INF INF

答案

a n s = m i n { d p n + 1 , 0 , k } , k ∈ [ − 8 , 0 ] ans = min \{dp_{n + 1, 0, k}\}, k \in [-8, 0] ans=min{dpn+1,0,k},k[8,0](之所以是 n + 1 n + 1 n+1,是因为状态设计中第一维 i i i 是表示 [ 1 , i − 1 ] [1, i - 1] [1,i1] 中的同学全部打完饭)。


代码

#include <bits/stdc++.h>

using namespace std;

const int maxn = 1e3 + 7;
const int maxs = (1 << 8) + 7;
const int inf  = 0x3f3f3f3f;

int qr() {
	int x = 0, f = 1; char c = getchar();
	while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
	while (c >= '0' && c <= '9') {x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}
	return x * f;
}

int n;
int t[maxn], b[maxn];

// dp[i][s][j] 表示考虑第 i 个人时
// 他以及他后面 7 个人(一共 8 个人) 的吃饭状态为 s
// 最后一个吃饭的人为 i + k 状态下的最少时间, k ∈ [-8, 7]
int dp[maxn][maxs][25];
void sol() {
	memset(dp, inf, sizeof(dp));
	memset(t, 0, sizeof(t));
	memset(b, 0, sizeof(b));
	n = qr();
	for (int i = 1; i <= n; ++i) t[i] = qr(), b[i] = qr();
	
	dp[1][0][-1 + 10] = 0;
	for (int i = 1; i <= n; ++i) {
		for (int s = 0; s < (1 << 8); ++s) {
			for (int j = -8; j <= 7; ++j) {
				if (dp[i][s][j + 10] == inf) continue;
				if (s & 1) dp[i + 1][s >> 1][j - 1 + 10] = min(dp[i + 1][s >> 1][j - 1 + 10], dp[i][s][j + 10]);
				else {
					int r = inf;
					for (int k = 0; k < 8; ++k) if (!(s & (1 << k))){
						if (i + k > r) break;
						r = min(r, i + k + b[i + k]);
						dp[i][s | (1 << k)][k + 10] = 
							min(dp[i][s | (1 << k)][k + 10], dp[i][s][j + 10] + (i + j == 0 ? 0 : (t[i + k] ^ t[i + j])));
					}
				}
			}
		}
	}
	int ans = inf;
	for (int k = -8; k <= -1; ++k)
		ans = min(ans, dp[n + 1][0][k + 10]);
	printf("%d\n", ans);
}
int main() {
	int T = qr();
	while (T--) sol();
	return 0;
}

http://www.kler.cn/a/595038.html

相关文章:

  • 清华最新出品YOLOE: 开放检测与分割模型、支持多提示机制与零样本迁移【附源码与论文】
  • ClickHouse 深度解析:列式存储为何成为 OLAP 的「核武器」
  • 【Java基础】在Java中,一个线程的大小(即线程所占用的内存)是多少
  • 嵌入式Linux——Framebuffer应用编程
  • 算法系列——有监督学习——4.支持向量机
  • 【Pandas】pandas Series plot.barh
  • 区块链交易所平台开发全解析
  • Microsoft Edge浏览器的取证分析(基于Chromium)
  • GitHub在push推送到远程仓库的时候显示Logon failed登录失败
  • Powershell美术资产批量重命名
  • 使用系统Picker
  • Performance Hub Active Report
  • Deepseek X 文心智能体:提示词工程猫
  • 编写一个简单的chrome截图扩展
  • 一文了解 分布式训练
  • C# 集合(Collection)详解以及区别
  • 【记录一下】Microsoft登录反复跳转【需要家长或监护人同意才能使用该帐户】页面
  • Python:文件的基本操作与基本读写
  • RAGFlow爬虫组件使用及ragflow vs dify 组件设计对比
  • 用@keyframes-animation来实现动画效果