[noi707]LP

(以下用$Sa=\sum_{j=1}^{i}xi\cdot ai$,Sb和Sc同理)
令f[i][x]表示前i个数,$Sa\le x\le Sb$时最小的Sc
考虑第i个数是否选择,可以得到递推式$f[i][x]=min(f[i-1][x],min(f[i-1][x-j])+ci)$(j满足$ai\le j\le bi$),这个东西用单调队列维护即可
(这个转移的正确性可以用充分和必要两方面来考虑,具体不证了)

 1 #include
 2 using namespace std;
 3 #define N 1005
 4 #define oo 0x3f3f3f3f
 5 int t,n,m,l,r,a[N],b[N],c[N],q[N*10],f[N][N*10];
 6 int main(){
 7     scanf("%d",&t);
 8     while (t--){
 9         scanf("%d%d",&n,&m);
10         for(int i=1;i<=n;i++)scanf("%d",&a[i]);
11         for(int i=1;i<=n;i++)scanf("%d",&b[i]);
12         for(int i=1;i<=n;i++)scanf("%d",&c[i]);
13         for(int i=1;i<=m;i++)f[0][i]=oo;
14         f[0][0]=0;
15         for(int i=1;i<=n;i++){
16             l=1;
17             r=0;
18             for(int j=0;j<=m;j++){
19                 if (a[i]<=j){
20                     while ((l<=r)&&(f[i-1][j]<=f[i-1][q[r]]))r--;
21                     q[++r]=j-a[i];
22                 }
23                 while ((l<=r)&&(q[l];
24                 f[i][j]=f[i-1][j];
25                 if (l<=r)f[i][j]=min(f[i][j],f[i-1][q[l]]+c[i]);
26             }
27         }
28         if (f[n][m]==oo)printf("IMPOSSIBLE!!!\n");
29         else printf("%d\n",f[n][m]);
30     }
31 }
View Code

 

你可能感兴趣的:([noi707]LP)