AcWing 318. 划分大理石 多重背包三

#include<bits/stdc++.h>
using namespace std;
const int N=2e5;
int f[N];
int g[N];
int q[N];
int s1[7];
int m;
//数量为a[1...6]
//还是数量

//价值1~6
//变为体积

//然后价值都是1

//问能不能凑出体积为一半
int main() {
    while(cin>>s1[1]>>s1[2]>>s1[3]>>s1[4]>>s1[5]>>s1[6]) {
        m=0;
        for(int i=1; i<=6; i++)
            m+=i*s1[i];
        if(m==0)
            break;
        if(m&1)
            cout<<"Can't"<<endl;
        else {
            m>>=1;
            for (int i = 1; i <= 6; i ++ ) {
                int v=1,w=1,s=s1[i];
                memcpy(g, f, sizeof f);//存储上一层,因为会用到上一层的数数据
                for (int j = 0; j < v; j ++ ) {//余数
                    int hh = 0, tt = -1;//滑动窗口,队列
                    for (int k = j; k <= m; k += v) { //表示每一个体积
                        if (hh <= tt && q[hh] < k - s * v)//当队列不空,而且已经滑出窗口
                            hh ++ ;
                        if (hh <= tt)//如果队列不空,更新当且最大值
                            //最大值位队头元素
                            //q[hh]对应最大值对应下标,基础值再加上收益
                            //收益是指里面装了多少个第i个物品,多少个:k-q[hh]/v个,再乘价值
                            //中间空余了多少个第i个物品的体积    空余一个,就可以多放一个物品进来
                            f[k] = max(f[k], g[q[hh]] + (k - q[hh]) / v * w);
                        //弹出没有用的元素       下标减去初始余数
                        //当队列不空,而且队尾价值小于当前价值,队尾就没有意义,弹出
                        while (hh <= tt && g[q[tt]] - (q[tt] - j) / v * w <= g[k] - (k - j) / v * w)
                            tt -- ;
                        q[ ++ tt] = k;//存每个体积
                    }
                }
            }
            if(f[m]==0)
                cout<<"Can't"<<endl;
            else
                cout<<"Can"<<endl;
        }
    }
    return 0;
}

posted @ 2020-03-31 13:19  晴屿  阅读(112)  评论(0编辑  收藏  举报