欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 会展 > 洛谷 P2157 [SDOI2009] 学校食堂

洛谷 P2157 [SDOI2009] 学校食堂

2025/4/5 10:00:14 来源:https://blog.csdn.net/m0_72761451/article/details/146429020  浏览:    关键词:洛谷 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;
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词