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

xyz32768
“各方面相差太远”搬运于
2025-08-24 21:34:36,当前版本为作者最后更新于2017-08-17 23:28:09,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
状压。
设表示第个人到第个人已经打完饭,第个人以及后面个人是否打饭的状态为,当前最后一个打饭的人的编号为(的范围为到,所以用数组存时要加上),那么转移为:
当j&1为真,就表示第个人已经打完饭,之后的个人中,还没打饭的人就再也不会插入到第个人前面了。所以这时候可以转移到,即$f[i+1][j>>1][k-1]=min(f[i+1][j>>1][k-1],f[i][j][k])$,不需要累积时间(因为在j&1为真的情况下,和的意义是一样的)。
而为什么意义是一样的呢?因为可以看出,最后一个打饭的人的编号为,和表示的一样。而第个人也打完了饭,所以满足「第个人到第个人已经打完饭」这个条件。而就是说之后的第个人就是之后的第个人(就是本人),之后的第个人就是之后的第个人,之后的第个人就是之后的第个人,…。这样就可以看出意义一样了。
当j&1为假时,是没办法转移到的(因为之前的人还有没有打完饭)。但是这时候可以把以及之后的个人中选出一个人打饭,也就是枚举从到,$f[i][j|(1<<h)][h]=min(f[i][j|(1<<h)][h],f[i][j][k]+time(i+k,i+h))$,其中表示如果上一个人编号为,当前的人编号为,那么做编号为的人的菜需要的时间。 当然,这个转移需要考虑到忍耐度的问题。这样,在和之后的个人,不是每一个还未打饭的人都可以先打饭的。因为编号在他之前的所有未打饭的人的忍耐度必须能忍受这个人在他们之前打饭。所以,在这里用了一个变量来统计了一下,表示到目前为止的未打饭的人的忍受范围(注意,不是忍耐度,忍受范围是指能忍受在其之前打饭的最大位置)的最小值,对于任何一个人,如果,就表示他无法满足编号在他之前的所有人的忍受范围,就不要考虑这个人了。代码实现如下:
lir = INF; for (h = 0; h <= 7; h++) if (!((j >> h) & 1)) { if (i + h > lir) break; chkmin(lir, i + h + B[i + h]); chkmin(f[i][j | (1 << h)][h + 8], f[i][j][k + 8] + (i + k ? (T[i + k] ^ T[i + h]) : 0)); }其中为上面提到的统计变量,为如果则把赋为。
最后答案即为。
完整代码:
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; inline int read() { int res = 0; bool bo = 0; char c; while (((c = getchar()) < '0' || c > '9') && c != '-'); if (c == '-') bo = 1; else res = c - 48; while ((c = getchar()) >= '0' && c <= '9') res = (res << 3) + (res << 1) + (c - 48); return bo ? ~res + 1 : res; } const int N = 1005, INF = 0x3f3f3f3f; int n, T[N], B[N], f[N][1 << 8][20]; void chkmin(int &a, int b) {a = min(a, b);} void work() { int i, j, k, h, lir; n = read(); for (i = 1; i <= n; i++) T[i] = read(), B[i] = read(); memset(f, INF, sizeof(f)); f[1][0][7] = 0; for (i = 1; i <= n; i++) for (j = 0; j < (1 << 8); j++) for (k = -8; k <= 7; k++) if (f[i][j][k + 8] != INF) { if (j & 1) chkmin(f[i + 1][j >> 1][k + 7], f[i][j][k + 8]); else { lir = INF; for (h = 0; h <= 7; h++) if (!((j >> h) & 1)) { if (i + h > lir) break; chkmin(lir, i + h + B[i + h]); chkmin(f[i][j | (1 << h)][h + 8], f[i][j][k + 8] + (i + k ? (T[i + k] ^ T[i + h]) : 0)); } } } int res = INF; for (k = 0; k <= 8; k++) res = min(res, f[n + 1][0][k]); printf("%d\n", res); } int main() { int T = read(); while (T--) work(); return 0; }
- 1
信息
- ID
- 1160
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者