zoukankan      html  css  js  c++  java
  • 【题解】SDOI2009学校食堂

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

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

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

      感觉我的状压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
    ", ans);
        }
        return 0;
    }
  • 相关阅读:
    QSetting
    类中函数前、后、参数加const
    delete指针
    自定义数组类
    手动调用构造函数
    windows和linux平台下的通用时间测试函数
    多线程编程学习
    Android 利用ImageView显示图片
    特征描述算子-sift
    opencv边界扩展
  • 原文地址:https://www.cnblogs.com/twilight-sx/p/9112346.html
Copyright © 2011-2022 走看看