zoukankan      html  css  js  c++  java
  • P2157 [SDOI2009]学校食堂

    传送门

    做菜主要是按时间顺序,所以可以考虑DP

    但是可能后面的人会先打饭

    可以发现同学最多只能让后面的第7个同学先打饭

    可以从这里入手考虑问题

    把每8个一起的同学看成一个状态

    在他们之前的人都已经打好饭了

    想象一个从左往右的队列

    从1~i-1 的同学都打完饭了

    然后需要知道的状态是 i~i+7 共8个同学的总状态

    显然如果第 i 个同学已经打好了

    那么 1~i 的同学都好了

    所以可以转到下一个 i

    设 f [ i ] [ j ] 表示从1~i-1 的同学都好了,第 i~i+7 的总状态为 j 时需要的最少时间

    那么 如果 j&1 ==1 说明第 i 个同学已经好了

    这时可以转到 f[ i+1 ] [ j>>1 ] (显然)

    但是具体转的时候发现:

    不知道上一个打饭的人是谁,就不知道这次需要多少时间...

    但是可以发现

    上一个打饭的人肯定在 i-8 ~ i+7 之间(不然肯定有同学不能容忍了)

    所以多开一维

    设 f [ i ] [ j ] [ k ]   i 和 j 含义不变, k 表示上一个打饭的人是 i + k (-8<=k<=7,存的时候要加8)

    那么 如果 j&1 ==1 f[ i ] [ j ] [ k ] 就可以转到 f [ i+1 ] [ j>>1 ] [ k-1 ] (注意这时并没有人打饭,只是两个状态表示的含义相同,可以直接转移)

    然后考虑给一个人打饭

    枚举 i ~ i+7 的人 h(h是与 i 的相对距离,和 k 一样)

    那么 f [ i ] [ j ] [ k ] 可以转到 f [ i ] [ j|(1<<h) ] [ h ]

    并且增加的时间是 (a[ i+k ]|a[ i+h ]) - (a[ i+k ]&a[ i+h ])  =  a[ i+k ] ^ a[ i+h ]

    注意判断 h 的合法性

    显然一个同学如果打过饭了就不能再打   (j&(1<<h) != 1)

    还要注意左边同学的容忍度   (j&(1<<l)!=1 && h-l<=b[ i+l ] && i<=l<h)

    然后就可以转移了

    最后在 f [ n+1 ] [ 0 ] [ i ] 之间取 min 就好了

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int N=2007,INF=0x3f3f3f3f;
    int T,n,a[N],b[N];
    int f[N][307][17];
    inline bool pd(int &i,int &j,int &h)//判断合法性
    {
        if(j&(1<<h)) return 1;
        for(int l=0;l<h;l++)
            if(h-l>b[i+l]&& !(j&(1<<l)) ) return 1;
        return 0;
    }
    int main()
    {
        cin>>T;
        while(T--)
        {
            //f[i][j][k] -> f[i+1][j>>1][k-1]  ( j&1 && k )
            //f[i][j][k] -> f[i][j&(1<<h)][h]+(a[i+k]^a[i+h])  ( !j&(1<<h) && ( h-l<=b[i+l] (!j&(1<<l)&&0<=l<h) ) )
            memset(f,INF,sizeof(f));
            for(int i=1;i<=7;i++) f[1][0][i]=0;
    
            cin>>n;
            for(int i=1;i<=n;i++) scanf("%d%d",&a[i],&b[i]);
    
            for(int i=1;i<=n;i++)
                for(int j=0;j<(1<<8);j++)
                    for(int k=-8;k<8;k++)
                        if(f[i][j][k+8]!=INF)
                        {
                            if(j&1 && k!=-8) f[i+1][j>>1][k+7]=min(f[i+1][j>>1][k+7],f[i][j][k+8]);
                            for(int h=0;h<8;h++)
                            {
                                if(pd(i,j,h)) continue;
                                f[i][j|(1<<h)][h+8]=min(f[i][j|(1<<h)][h+8],f[i][j][k+8]+ (i+k>0/*注意第一个同学不用时间*/ ? (a[i+k]^a[i+h]) : 0) );
                            }
                        }
            int ans=INF;
            for(int i=0;i<16;i++) ans=min(ans,f[n+1][0][i]);
            printf("%d
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    Java 第十一届 蓝桥杯 省模拟赛 洁净数
    Java 第十一届 蓝桥杯 省模拟赛 第十层的二叉树
    Java 第十一届 蓝桥杯 省模拟赛 第十层的二叉树
    Java 第十一届 蓝桥杯 省模拟赛 第十层的二叉树
    Java 第十一届 蓝桥杯 省模拟赛 70044与113148的最大公约数
    Java 第十一届 蓝桥杯 省模拟赛 70044与113148的最大公约数
    20. Valid Parentheses
    290. Word Pattern
    205. Isomorphic Strings
    71. Simplify Path
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/9699213.html
Copyright © 2011-2022 走看看