zoukankan      html  css  js  c++  java
  • 洛谷 P2051 [SDOI2009]学校食堂

    传送门


    题目分析:
    首先,我们先看看做菜时间的运算机制。
    $(A~ exttt{or}~B)-(A~ exttt{and}~B)$这个试子看起来有点复杂(因为我太菜了),仔细想想,是不是可以转化为$A~ exttt{xor}~B$呢?
    好了,我们已经知道了运算机制,并将其化简了,接下来再看看其他的东东。


    我太菜了,所以只能简单地理解为:
    给定$n$个人, 确定一个排列, 使得不存在$i+b_{i}$在$i$之前。
    并最小化下面这个式子。
    $sum_{i=2}^{n}t_{i}$ $ exttt{xor}$ $t_{i-1}$应该都看得懂吧。
    然后我们再看看数据范围。
    $1<=bi<=7$。所以我们可以考虑状压DP 其实我是点开了标签才知道是状压的

    往最简单的想:
    $f[i][j]$表示前$i-1$个人已经打了饭,第$i$个人打饭,包括ta自己的后面的人打饭的集合。如,0101中0就是没打,1就是打。
    但是显然题目里面允许$b_{i}$个同学插队,所以我们要记录一下前面打饭的人。

    所以最终状态定义为:
    $f[i][j][k]$表示前$i−1$个人都已经打完饭,$ i∼i+7$的打饭集合为$j$, 上一个打饭的是$i+k$.
    为了方便理解,我的代码里面for循环是$k=-8~7$,所以宏定义了一下,把$k$加了一个10。
    其实这个10可以随意,只要大于8就可以了,因为c++数组不能为负数。
    这样处理,可以方便理解,减少打代码时的错误,而且数组相对位置没变,对答案没有影响。
    扯了那么多,接下来就是递推了。

    因为是求最小值,所以最开始$f$数组赋为$inf$,边界$f[1][0][-1]=0$。
    然后枚举$2^8$种情况,即256,在这些0101001序列中:
    当$j&1!=0$时,说明ta已经打了饭,后面的人就不会跑到他前面。
    我们发现这个状态和 $f[i+1][j>>1][k−1]$ (第$i+1$个人打饭, 集合为去掉$i$后的状态, 最后一个打饭的人是$(i+1)+(k−1)$是一样的.可以直接转移过去.
    当$j&1==0$时,说明ta还没有打饭,那么此时我们就在后面的$b_{i}$人里面枚举,去一个最小值。
    还要注意的是,第一道菜没有代价,所以特判一下。
    其次,为了满足同学的容忍度,如果同学忍无可忍了,就$break$出循环。


    代码:

    #include <bits/stdc++.h>
    #define f(i,j,k) f[i][j][k+10]
    using namespace std;
    
    const int N=1010;
    int n,t[N],b[N],f[N][260][20];
    
    int main()
    {
        ios::sync_with_stdio(0);
        int T;
        cin>>T;
        while (T--) {
            cin>>n;
            for (int i=1;i<=n;i++)
            cin>>t[i]>>b[i];
            memset(f,0x3f,sizeof(f));
            f(1,0,-1)=0;
            for (int i=1;i<=n;i++)
            for (int j=0;j<256;j++)
            for (int k=-8;k<8;k++)
            if (f(i,j,k)<1e9) {
                if (j&1)
                f(i+1,j>>1,k-1)=min(f(i+1,j>>1,k-1),f(i,j,k));
                else {
                    int r=1e9;
                    for (int l=0;l<8;l++)
                    if (!(j&(1<<l))) {
                        if (i+l>r) break;
                        if (i+l+b[i+l]<r) r=i+l+b[i+l];
                        f(i,j|(1<<l),l)=min(f(i,j|(1<<l),l),f(i,j,k)+(i+k?(t[i+k]^t[i+l]):0));
                    }
                }
            }
            int ans=1e9;
            for (int i=-8;i<=0;i++)
            ans=min(ans,f(n+1,0,i));
            cout<<ans<<endl;
        }
        return 0;
    }
  • 相关阅读:
    顺序栈用C语言实现
    对队列的操作和算法
    对链表的操作与算法
    对动态数组的操作与算法
    链表
    冒泡排序
    指针之动态分配内存
    字符串匹配KMP算法
    DS二叉树--层次遍历
    DS图--最小生成树
  • 原文地址:https://www.cnblogs.com/fushao2yyj/p/8710990.html
Copyright © 2011-2022 走看看