zoukankan      html  css  js  c++  java
  • 6358. 【NOIP2019模拟2019.9.15】小ω的仙人掌

    题目

    题目大意

    给你一串二元组((a_i,b_i))的数列。
    求最小的区间([l,r])长度,满足([l,r])中的每个二元组选或不选,使得(sum a_i=w)(sum b_ileq 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
    ");
    	else
    		printf("%d
    ",ans);
    	return 0;
    }
    

    总结

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

  • 相关阅读:
    JavaScript等比例缩放图片
    乐器的研究
    乐器的研究
    单位的理解
    单位的理解
    那些狗,那些人
    Opencv+Zbar二维码识别(标准条形码/二维码识别)
    二维码解码器Zbar+VS2012开发环境配置
    条形码、二维码的区别和组成结构介绍
    Caffe-Windows下遇到过的问题、技巧、解决方案
  • 原文地址:https://www.cnblogs.com/jz-597/p/11536140.html
Copyright © 2011-2022 走看看