一名苦逼的OIer,想成为ACMer

Iowa_Battleship

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

BZOJ原题链接

洛谷原题链接

注意到\(B[i]\)很小,考虑状压\(DP\)
\(f[i][j][k]\)表示前\(i - 1\)个人已经拿到菜,第\(i\)个人及其后面\(7\)个人是否拿到菜的状态为\(j\),上一个拿到菜的人的编号为\(i + k,-8\leqslant 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 \to 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 ^ {-8\leqslant 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\n", mi);
	}
	return 0;
}

posted on 2018-10-24 15:53  Iowa_Battleship  阅读(90)  评论(0编辑  收藏  举报

导航