【题解】SDOI2009学校食堂

  不知道有没有人跟我有一样的感觉……实际上很多的状压DP都不难,然而调到心碎……这题体面看起来很长,还有混合的‘位运算’来吓唬人(实际上就是异或而已)。但实际上只要仔细阅读,发现也是一道水水的裸题。

  首先,题目当中给出的信息是:\(B_{i} <= 7\)。看到这一条,心中已有八分笃定:在这样的环境下,估计是状压。然后就开始考虑转移的方程: 先从暴力的状态开始,我们要确定没有后效性的状态,则有两个维度应该是必须的。一维代表 \(i\),即现在 \(1 -> i\) 之间的同学都已经打到饭了,以及 \(j\) 即上一名打饭的同学的口味值。最后的一维状压,压 \(\left (i + 1, i + 8  \right )\) 号同学的打饭状态,后面的就不用了,因为第 \(i + 1\) 个人目前还没有打到饭,他最大只能容忍第 \(i + 8\) 名同学先打。

  可是出现了一个问题:\(j\)的范围过大。所以不能存值,只能存编号。考虑在第\(i + 1\) 个人还没有打饭的情况下,上一个打饭的人只能在范围 \(\left (i - 7, i + 8  \right )\) 中,我们存下这一个编号,并且规定一个标准:第 \(i\) 名同学的编号为 \(7\)。这样,所有可能的同学编号均在 \(\left (0, 15 \right )\) 的范围内。

  感觉我的状压dp代码有毒……食用需谨慎呐 ̄へ ̄

#include <bits/stdc++.h>
using namespace std;
#define maxn 1005
#define INF 9999999
int T, CNST;
int n, a[maxn], t[maxn];
int f[maxn][170][505];

int read()
{
    int x = 0, k = 1;
    char c;
    c = getchar();
    while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); }
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * k;
}

void Init()
{
    for(int i = 0; i <= n; i ++)
        for(int j = 0; j <= 15; j ++)
            for(int k = 0; k < CNST; k ++)
                f[i][j][k] = INF;
}

void update(int &x, int y) { x = x < y ? x : y; }

int main()
{
    T = read(), CNST = (1 << 8) - 1;
    while(T --)
    {
        n = read();
        Init();
        for(int i = 1; i <= n; i ++) a[i] = read(), t[i] = read();
        f[0][7][0] = 0;
        for(int i = 0; i < n; i ++)
            for(int k = 0; k < CNST; k ++)
            {
                for(int j = 0; j <= 15; j ++)
                {
                    if(f[i][j][k] >= INF) continue;
                    int tmp = k, minn = INF;
                    for(int s = 0; s <= 7 && (i + s + 1) <= n; s ++)
                        if(!((tmp >> s) & 1)) minn = min(minn, s + t[i + s + 1]); 
                    if(minn == INF || k & 1) minn = 0;
                    for(int s = 1; s <= minn; s ++)
                    {
                        if((k >> s) & 1) continue;
                        if(i + s + 1 > n) break;
                        int q = i + j - 7 >= 0 ? a[i + j - 7] : 0; 
                        if(!i && !k) q = a[i + s + 1];
                        int ret = q ^ a[i + s + 1];
                        if(s + 8 <= 15) update(f[i][s + 8][k | (1 << s)], f[i][j][k] + ret);
                    }
                    int q = (i + j - 7) >= 0 ? a[i + j - 7] : 0;
                    if(!i && !k) q = a[i + 1]; 
                    update(f[i + 1][7][k >> 1], f[i][j][k] + (q ^ a[i + 1]));
                    if(j && (k & 1)) update(f[i + 1][j - 1][k >> 1], f[i][j][k]);
                }
            } 
        int ans = INF;
        for(int j = 0; j <= 15; j ++)
            for(int k = 0; k < CNST; k ++)
                ans = min(ans, f[n][j][k]);
        printf("%d\n", ans);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/twilight-sx/p/9112346.html