zoukankan      html  css  js  c++  java
  • Jzoj3223 Ede的新背包问题

    题意:多重背包,每次对于第i个物品不能选的情况求最大获益,n,m<1000,q<30000

    最简单的方法肯定是暴力,每次询问都做一次背包,显然这样会超时

    我们可以用二进制分拆法或者是用单调队列优化复杂度到O(qnm),可是依然不能过

    注意到每次不能取的部分只有一个,那我们考虑用前缀和后缀分别维护,最后合并两个部分的答案

    f[i][j]表示做到第i个物品的状态,g[i][j]表示倒着做i个物品的状态

    最后合并两边即可

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    int w[10000],v[10000],t[1000],N=0;
    int n,m,f[10010][1001],g[10010][1001];
    inline void ins(int W,int V){  //加入一个物品
    	if(!W||!V) return;
    	w[++N]=W;  v[N]=V;
    }
    int calc(int x,int E){  //合并两边的答案
    	if(!x) return g[t[1]][E];
    	if(x==n-1) return f[t[n-1]-1][E];
    	int *a=f[t[x]-1],*b=g[t[x+1]],ans=0;
    	for(int i=0;i<=E;++i) ans=max(ans,a[i]+b[E-i]);
    	return ans;
    }
    int main(){
    	scanf("%d",&n);
    	for(int a,b,c,i=0,j;i<n;++i){
    		scanf("%d%d%d",&a,&b,&c); t[i]=N+1;
    		for(j=0;1<<j<=c/2;++j) ins(a<<j,b<<j);  //二进制拆分
    		ins((c-(1<<j)+1)*a,(c-(1<<j)+1)*b);
    	}
    	for(int i=1;i<=N;++i){
    		memcpy(f[i],f[i-1],4040);
    		for(int j=1000;j>=w[i];--j)
    			f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i]);
    	}
    	for(int i=N;i;--i){
    		memcpy(g[i],g[i+1],4000);
    		for(int j=1000;j>=w[i];--j)
    			g[i][j]=max(g[i+1][j],g[i+1][j-w[i]]+v[i]);
    	}
    	scanf("%d",&m);
    	for(int a,b,i=0;i<m;++i){
    		scanf("%d%d",&a,&b);
    		printf("%d
    ",calc(a,b));
    	}
    }


  • 相关阅读:
    [考试反思]0904NOIP模拟测试37:守望
    游戏:最短路,拆点
    [考试反思]0903NOIP模拟测试36:复始
    [考试反思]0902NOIP模拟测试35:摆动
    长寿花:dp
    [考试反思]0901NOIP模拟测试34:游离
    赤壁情:dp
    [考试反思]0829NOIP模拟测试33:仰望
    [考试反思]0828NOIP模拟测试32:沉底
    宅男计划:单峰函数三分
  • 原文地址:https://www.cnblogs.com/Extended-Ash/p/9477354.html
Copyright © 2011-2022 走看看