[BZOJ1226][SDOI2009]学校食堂Dining
[BZOJ1226][SDOI2009]学校食堂Dining
试题描述
小F 的学校在城市的一个偏僻角落,所有学生都只好在学校吃饭。学校有一个食堂,虽然简陋,但食堂大厨总能做出让同学们满意的菜肴。当然,不同的人口味也不一定相同,但每个人的口味都可以用一个非负整数表示。由于人手不够,食堂每次只能为一个人做菜。做每道菜所需的时间是和前一道菜有关的,若前一道菜的对应的口味是a,这一道为b,则做这道菜所需的时间为(a or b)-(a and b),而做第一道菜是不需要计算时间的。其中,or 和and 表示整数逐位或运算及逐位与运算,C语言中对应的运算符为“|”和“&”。学生数目相对于这个学校还是比较多的,吃饭做菜往往就会花去不少时间。因此,学校食堂偶尔会不按照大家的排队顺序做菜,以缩短总的进餐时间。虽然同学们能够理解学校食堂的这种做法,不过每个同学还是有一定容忍度的。也就是说,队伍中的第i 个同学,最多允许紧跟他身后的Bi 个人先拿到饭菜。一旦在此之后的任意同学比当前同学先拿到饭,当前同学将会十分愤怒。因此,食堂做菜还得照顾到同学们的情绪。现在,小F 想知道在满足所有人的容忍度这一前提下,自己的学校食堂做完这些菜最少需要多少时间。
输入
第一行包含一个正整数C,表示测试点的数据组数。每组数据的第一行包含一个正整数N,表示同学数。每组数据的第二行起共N行,每行包含两个用空格分隔的非负整数Ti和Bi,表示按队伍顺序从前往后的每个同学所需的菜的口味和这个同学的忍受度。每组数据之间没有多余空行。
输出
包含C行,每行一个整数,表示对应数据中食堂完成所有菜所需的最少时间。
输入示例
2 5 5 2 4 1 12 0 3 3 2 2 2 5 0 4 0
输出示例
16 1
数据规模及约定
对于30%的数据,满足1 ≤ N ≤ 20。对于100%的数据,满足1 ≤ N ≤ 1,000,0 ≤ Ti ≤ 1,000,0 ≤ Bi ≤ 7,1 ≤ C ≤ 5。存在30%的数据,满足0 ≤ Bi ≤ 1。存在65%的数据,满足0 ≤ Bi ≤ 5。存在45%的数据,满足0 ≤ Ti ≤ 130。
题解
发现 Bi 非常小,这样一个合法的选择状态一定长成这个样子:我们考虑前 i + 8 个学生,前面 i 个学生都处理完了,只有最后 8 个学生是不确定的。于是我们不难想到把最后 8 个学生的处理状态压成一个二进制。
于是一个初步的状态设计就出来了:f(i, S, j) 表示前 i 个学生都处理完了,最后 8 个学生的处理状态为 S,上一次处理的学生要求的美味度为 j。但是状态数有 1000 * 256 * 1000,转移数有 8,会超时,并且如果不用滚动数组还会 MLE。
那么改一下最后一维状态,上一次处理的学生到最后一个位置(即位置 i + 8)的距离不会超过 15(想一想,为什么),于是我们把 j 换成“上一个学生到 i + 8 的距离”就好了。
暴力 dp:
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> using namespace std; const int BufferSize = 1 << 16; char buffer[BufferSize], *Head, *Tail; inline char Getchar() { if(Head == Tail) { int l = fread(buffer, 1, BufferSize, stdin); Tail = (Head = buffer) + l; } return *Head++; } int read() { int x = 0, f = 1; char c = Getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); } return x * f; } #define maxn 1010 #define maxv 1002 #define maxs 256 #define oo 2147483647 int n, A[maxn+10], Back[maxn+10], f[2][maxs][maxv]; void up(int& a, int b) { a = min(a, b); return ; } int main() { int T = read(); while(T--) { n = read(); for(int i = 1; i <= n; i++) A[i] = read(), Back[i] = read(); int all = (1 << 8) - 1, use[10], cntu; for(int i = 0; i < 2; i++) for(int S = 0; S <= all; S++) for(int j = 0; j < maxv; j++) f[i][S][j] = oo; f[0][0][1001] = 0; bool cur = 0; for(int i = 0; i < n; i++, cur ^= 1) { for(int S = 0; S <= all; S++) for(int j = 0; j < maxv; j++) f[cur^1][S][j] = oo; for(int S = 0; S <= all; S++) { if(S & 1) { for(int j = 0; j < maxv; j++) up(f[cur^1][S>>1][j], f[cur][S][j]); continue; } int mnr = min(n, i + 1 + Back[i+1]); for(int p = 1; p < 8 && i + 1 + p <= n; p++) if(!(S >> p & 1)) up(mnr, i + 1 + p + Back[i+1+p]); cntu = 0; for(int p = 1; p < 8 && i + 1 + p <= n; p++) if(!(S >> p & 1) && i + 1 + p <= mnr) use[++cntu] = i + 1 + p; for(int j = 0; j < maxv; j++) if(f[cur][S][j] < oo) { for(int x = 1; x <= cntu; x++) { int p = use[x]; up(f[cur][S|(1<<p-1-i)][A[p]], f[cur][S][j] + (j == maxv - 1 ? 0 : (j | A[p]) - (j & A[p]))); } up(f[cur^1][S>>1][A[i+1]], f[cur][S][j] + (j == maxv - 1 ? 0 : (j | A[i+1]) - (j & A[i+1]))); } } } int ans = oo; for(int j = 0; j < maxv; j++) up(ans, f[cur][0][j]); printf("%d\n", ans); } return 0; }
AC:
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> using namespace std; int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 1010 #define maxs 256 #define maxd 16 #define oo 2147483647 int n, A[maxn], Back[maxn], f[maxn][maxs][maxd]; void up(int& a, int b) { a = min(a, b); return ; } int main() { int T = read(); while(T--) { n = read(); for(int i = 1; i <= n; i++) A[i] = read(), Back[i] = read(); for(int i = 0; i <= n; i++) for(int S = 0; S < maxs; S++) for(int d = 0; d < maxd; d++) f[i][S][d] = oo; f[0][0][0] = 0; for(int i = 0; i <= n; i++) for(int S = 0; S < maxs; S++) { if(S & 1) { for(int d = 0; d < maxd; d++) if(f[i][S][d] < oo) up(f[i+1][S>>1][d+1], f[i][S][d]); continue; } int use[10], cntu = 0, mnr = n; for(int j = 0; j < 8 && i + j + 1 <= n; j++) if(!(S >> j & 1)) up(mnr, Back[i+j+1] + i + j + 1); for(int j = 0; j < 8 && i + j + 1 <= mnr; j++) if(!(S >> j & 1)) use[++cntu] = i + j + 1; for(int d = 0; d < maxd; d++) if(f[i][S][d] < oo) { int lv = (!i && !S) ? -1 : A[i+8-d]; for(int x = 1; x <= cntu; x++) { int p = use[x]; up(f[i][S|(1<<p-i-1)][i+8-p], f[i][S][d] + (lv < 0 ? 0 : (A[p] | lv) - (A[p] & lv))); } } } int ans = oo; for(int d = 0; d < maxd; d++) up(ans, f[n][0][d]); printf("%d\n", ans); } return 0; }