ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

6358. 【NOIP2019模拟2019.9.15】小ω的仙人掌

2019-09-17 18:53:17  阅读:326  来源: 互联网

标签:背包 15 2019.9 int 栈顶 NOIP2019 队列 ans include


题目

题目大意

给你一串二元组\((a_i,b_i)\)的数列。
求最小的区间\([l,r]\)长度,满足\([l,r]\)中的每个二元组选或不选,使得\(\sum a_i=w\)且\(\sum b_i\leq k\)


思考历程

想了好久,想来想去都是一个背包……
最终决定打暴力……


正解

先说说GMH大爷的神奇解法。
首先是二分答案\(ans\),转化成判定问题。然后在数列中每\(ans\)个点设置一个观测点。
以每个观测点为中心,向左和向右背包,然后合并。

然而正解并不需要一个\(\log\)
考虑双指针,就是记一个当前的最佳答案\(ans\),后面的区间长度都要小于\(ans\)。脑补一下这个过程,其实这就是一个队列,只需要支持左边出右边入的队列。
但是背包问题不满足可减性。于是就有个非常骚的解法:
把这个队列用两个栈来代替,栈顶分别为队头和队尾。
加入的时候,就在第二个栈的栈顶加入;弹出的时候,就直接弹出第一个栈的栈顶。
如果第一个栈为空,那就将第二个栈里的东西倒过来放到第一个栈中,然后暴力重构。
每个元素只会暴力重构一次,所以不会时间超限。


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
#define N 10010
#define maxW 5010
inline int input(){
    char ch=getchar();
    while (ch<'0' || '9'<ch)
        ch=getchar();
    int x=0;
    do{
        x=x*10+ch-'0';
        ch=getchar();
    }
    while ('0'<=ch && ch<='9');
    return x;
}
int n,W,K,a[N],b[N];
int f[N][maxW];
int st1[N],top1,st2[N],top2;
inline void update(int &a,int b){a>b?a=b:0;}
inline bool ok(int j){
    int jj=st1[top1];
    for (int k=0;k<=W;++k)
        if (f[jj][k]+f[j][W-k]<=K)
            return 1;
    return 0;
}
int main(){
    freopen("cactus.in","r",stdin);
    freopen("cactus.out","w",stdout);
    n=input(),W=input(),K=input();
    for (int i=1;i<=n;++i)
        a[i]=input(),b[i]=input();
    int ans=n+1;
    f[0][0]=0;
    for (int i=1;i<=W;++i)
        f[0][i]=K+1;
    for (int i=1,j=1;i<=n;++i){
        if (ok(st2[top2]))
            ans=j-i;
        for (;j<=n && j-i+1<ans;++j){
            st2[++top2]=j;
            int lst=st2[top2-1];
            memcpy(f[j],f[lst],sizeof(int)*(W+1));
            for (int k=0;k+a[j]<=W;++k)
                update(f[j][k+a[j]],f[lst][k]+b[j]);
            if (ok(j))
                ans=j-i+1;
        }
        if (!top1){
            for (int j=top2;j>=1;--j)
                st1[++top1]=st2[j];
            top2=0;
            for (int j=1;j<top1;++j){
                int now=st1[j],lst=st1[j-1];
                memcpy(f[now],f[lst],sizeof(int)*(W+1));
                for (int k=0;k+a[now]<=W;++k)
                    update(f[now][k+a[now]],f[lst][k]+b[now]);
            }
        }
        top1--;
    }
    if (ans==n+1)
        printf("-1\n");
    else
        printf("%d\n",ans);
    return 0;
}

总结

还有这么骚的栈操作……
这告诉我们有时候维护队列的东西可以用两个栈来搞。

标签:背包,15,2019.9,int,栈顶,NOIP2019,队列,ans,include
来源: https://www.cnblogs.com/jz-597/p/11536140.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有