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

Description

在这里插入图片描述
s<=1e4,ai<=w<=5e3,k<=1e9

Solution

  • 如果[l,r]合法,那么[l,r+1]也合法。
  • 所以可以用两个指针来维护,合法l++,不合法r++。
  • 现在我们要考虑对于指针l,r,判断[l,r]是否合法。
  • 我们想要维护[l,r]的背包的情况,往后加一个是w的,但是我们怎么删除头呢?
  • 有一种很巧妙的方法,我们使用双栈,将[l,r]分成[l,mid],[mid+1,r]两个区间,对于[l,mid]从后往前维护背包的数组,直接退就好了,对于[mid+1,r]维护前往后的。
  • 如果左边的栈没有了,就暴力将[mid+1,r]全部变成左边的栈。
  • 这样就是O(sw)的了。
  • 这种方法的性质为:O(w)判断,O(w)转移。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 10005
#define maxm 5005
#define inf 1e9+5
using namespace std;

int n,m,lim,i,j,k,a[maxn],b[maxn],ans;
int f[maxn][maxm],l,r,mid; 

void read(int &x){
	x=0; char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar());
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
}

int check(){
	if (mid==r) return f[l][m]<=lim;
	if (mid<l) return f[r][m]<=lim;
	for(i=0;i<=m;i++) if (f[l][i]+f[r][m-i]<=lim)
		return 1;
	return 0;
}

int main(){
	freopen("cactus.in","r",stdin);
	freopen("cactus.out","w",stdout);
	read(n),read(m),read(lim);
	for(i=1;i<=n;i++) read(a[i]),read(b[i]);
	for(i=1;i<=n;i++) for(j=0;j<=m;j++) f[i][j]=inf;
	l=1,r=0,mid=0,ans=n+1;
	while (r<=n){
		if (check()) {
			ans=min(ans,r-l+1);
			if (l>mid) {
				l++;
				mid=r;
				for(i=0;i<=m;i++) f[r][i]=inf;
				f[r][a[r]]=b[r],f[r][0]=0;
				for(i=r;i>l;i--) for(j=0;j<=m;j++) {
					f[i-1][j]=f[i][j];
					if (j>=a[i-1])
					f[i-1][j]=min(f[i-1][j],f[i][j-a[i-1]]+b[i-1]);
				}
			} else l++;
		} else{
			if (r==n) break;
			if (r==mid) {
				r++;
				f[r][a[r]]=b[r];
				f[r][0]=0;
			}  else {
				r++;
				for(i=0;i<=m;i++) {
					f[r][i]=f[r-1][i];
					if (i>=a[r])
						f[r][i]=min(f[r][i],f[r-1][i-a[r]]+b[r]);
				}
			}
		}
	}
	if (ans==n+1) printf("-1"); else printf("%d",ans);
}
原文地址:https://www.cnblogs.com/DeepThinking/p/13090964.html