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;
    }
    

    总结

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

  • 相关阅读:
    Asp.Net Web API 2第八课——Web API 2中的属性路由
    Asp.Net Web API 2第七课——Web API异常处理
    Asp.Net Web API 2第六课——Web API路由和动作选择
    Asp.Net Web API 2第五课——Web API路由
    开始学习python
    BMI 小程序 购物车
    深浅copy 文件操作
    字典 dict 集合set
    基本数据类型 (str,int,bool,tuple,)
    python 运算符
  • 原文地址:https://www.cnblogs.com/jz-597/p/11536140.html
Copyright © 2011-2022 走看看