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