zoukankan      html  css  js  c++  java
  • CodeForces

    ( ext{Description})

    传送门

    ( ext{Solution})

    首先看这个数据范围和个数总和为 (k) 的限制都很像背包吧。

    把每个数组拆成长度为 ([1,k]) 的前缀再做背包显然在时间和正确性上都不可取(不好控制每个数组只选一个)。

    我们都陷入怪圈。

    感觉很多这种题都有一个结论来限制时间。比如这题:数组是不降的。

    有一个结论:最多只有一个数组选了但是没有选完。

    考虑有 (i,j) 两个数组分别选到第 (p_i,p_j) 的数。如果 (a_{i,p_i}le a_{j,p_j}),又因为有 (a_{j,p_j}le a_{j,p_{j+1}}),那么舍 (p_i) 而取 (p_{j+1}) 也,而且新的数组肯定也有 (a_{i,p_i}le a_{j,p_j}),差距还会更大。那显然两个数组如果都选了没有选完,可以削弱其中一个,不停选另一个知道某个数组全部选或全选为止。

    考虑枚举那个选了没选完的数组,将其他数组抽象成一件物品,可以分别进行背包,时间复杂度 (mathcal O(n^2 imes k))

    如何优化?每次选一个数组都跑其它数组其实有很多重复,我们可以进行分治来优化。时间复杂度 (mathcal O(n imes k imes log n))

    详见代码。

    ( ext{Code})

    #include <cstdio>
    #define rep(i,_l,_r) for(signed i=(_l),_end=(_r);i<=_end;++i)
    #define print(x,y) write(x),putchar(y)
    
    template <class T> inline T read(const T sample) {
    	T x=0; int f=1; char s;
    	while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
    	while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
    	return x*f; 
    } 
    
    template <class T> inline void write(T x) {
    	if(x<0) return (void)(putchar('-'),write(-x));
    	if(x>9) write(x/10);
    	putchar(x%10^48);
    }
    
    #include <iostream>
    using namespace std;
    
    typedef long long ll;
    
    const int maxn=3005;
    
    int n,k,w[maxn][maxn];
    ll val[maxn],ans,tmp,f[maxn];
    
    void DP(int l,int r) {
    	if(l==r) { // 每个数组都会被选中成为选了但是没有选完的数组
    		tmp=0;
    		rep(i,1,w[l][0]) {
    			tmp+=w[l][i];
    			ans=max(ans,f[k-i]+tmp);
    		}
    		return;
    	}
    	int mid=l+r>>1;	ll t[maxn];
    	rep(i,0,k) t[i]=f[i];
    	rep(i,l,mid) for(int j=k;j>=w[i][0];--j) f[j]=max(f[j],f[j-w[i][0]]+val[i]);
    	DP(mid+1,r); // 保证那个数组在 [mid+1,r],那么 [l,mid] 就可以全部抽象成意见物品背包,这样就不用选中 [mid+1,r] 的每个数都做一次 [l,mid] 的背包
    	rep(i,0,k) f[i]=t[i];
    	rep(i,mid+1,r) for(int j=k;j>=w[i][0];--j) f[j]=max(f[j],f[j-w[i][0]]+val[i]);
    	DP(l,mid);
    }
    
    int main() {
    	int y;
    	n=read(9),k=read(9);
    	rep(i,1,n) {
    		w[i][0]=read(9);
    		rep(j,1,w[i][0]) {
    			y=read(9);
    			if(j<=k) w[i][j]=y,val[i]+=w[i][j];
    		}
    		w[i][0]=min(w[i][0],k);
    	}
    	DP(1,n);
    	print(ans,'
    ');
    	return 0;
    }
    
  • 相关阅读:
    MongoDB优化之一:常见优化方法
    Spark Streaming之一:整体介绍
    Java中实现MongoDB自增主键ID
    RDD之一:总体介绍
    对一致性Hash算法,Java代码实现的深入研究
    MongoDB 创建基础索引、组合索引、唯一索引以及优化
    mongo-查询
    MongoTemplate聚合操作
    RESTLET开发实例(一)基于JAX-RS的REST服务
    Hashtable的实现原理
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/14078847.html
Copyright © 2011-2022 走看看