• BZOJ1226或洛谷2157 [SDOI2009]学校食堂


    BZOJ原题链接

    洛谷原题链接

    注意到(B[i])很小,考虑状压(DP)
    (f[i][j][k])表示前(i - 1)个人已经拿到菜,第(i)个人及其后面(7)个人是否拿到菜的状态为(j),上一个拿到菜的人的编号为(i + k,-8leqslant k leqslant 7)时所用的最短时间。
    然后讨论状态的转移。

    • (j & 1)为真
      说明第(i)个人已经拿到菜,可以直接转移至(i + 1),即$$f[i + 1][j >> 1][k - 1] = min{ f[i + 1][j >> 1][k - 1], f[i][j][k] }$$
    • (j & 1)为假
      此时不能转移至(i + 1),因为第(i)个人还没拿到菜,不满足定义。
      于是我们可以枚举(q = 0 o 7),选出(i)后的第(q)个人去拿饭:$$f[i][j | (1 << h)][h] = min{ f[i][j | (1 << h)][h], f[i][j][k] + time(i + k, i + h) }$$
      (time(x,y))表示上一个拿菜的人的编号为(x),这次为(y),则需要做菜的时间。
      而在转移这种状态时,需要考虑每个人的容忍度,在循环(q)的过程中,维护一个最小的容忍度,若枚举到的人已经不被之前未拿菜的某人所容忍,那么就不需要考虑这个人与其之后的人了。

    最后答案为:(min limits ^ {-8leqslant k leqslant 0} { f[n + 1][0][k] })
    因为(k)可以为负,所以在储存是要整体右移。
    另外,题目中给出计算时间的公式((a | b) - (a & b)),实际上等于(a land b)

    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int N = 1010;
    const int M = (1 << 8) + 10;
    int f[N][M][20], T[N], B[N];
    inline int re()
    {
    	int x = 0;
    	char c = getchar();
    	bool p = 0;
    	for (; c < '0' || c > '9'; c = getchar())
    		p |= c == '-';
    	for (; c >= '0' && c <= '9'; c = getchar())
    		x = x * 10 + c - '0';
    	return p ? -x : x;
    }
    inline void ckminn(int &x, int y)
    {
    	if (x > y)
    		x = y;
    }
    int main()
    {
    	int i, j, k, n, m = M - 10, t, q, edr, mi;
    	t = re();
    	while (t--)
    	{
    		n = re();
    		for (i = 1; i <= n; i++)
    		{
    			T[i] = re();
    			B[i] = re();
    		}
    		memset(f, 60, sizeof(f));
    		f[1][0][7] = 0;
    		for (i = 1; i <= n; i++)
    			for (j = 0; j < m; j++)
    				for (k = -8 ; k <= 7; k++)
    					if (f[i][j][k + 8] < 1e8)
    					{
    						if (j & 1)
    							ckminn(f[i + 1][j >> 1][k + 7], f[i][j][k + 8]);
    						else
    						{
    							edr = 1e9;
    							for (q = 0; q <= 7; q++)
    								if (!(j & (1 << q)))
    								{
    									if (i + q > edr)
    										break;
    									ckminn(edr, i + q + B[i + q]);
    									ckminn(f[i][j | (1 << q)][q + 8], f[i][j][k + 8] + (i + k ? T[i + k] ^ T[i + q] : 0));
    								}
    						}
    					}
    		for (mi = 1e9, i = 0; i <= 8; i++)
    			ckminn(mi, f[n + 1][0][i]);
    		printf("%d
    ", mi);
    	}
    	return 0;
    }
    
  • 相关阅读:
    AcWing每日一题--数字三角形
    AcWing每日一题--货仓选址
    Codeforces Round #693
    动态规划--多重背包
    动态规划--完全背包
    动态规划--01背包
    博弈论--SG函数
    博弈论--Nim游戏
    基础数论--容斥定理
    基础数论--卡特兰数
  • 原文地址:https://www.cnblogs.com/Iowa-Battleship/p/9843951.html
Copyright © 2020-2023  润新知