[NAIPC2016]Jewel Thief(决策单调性+分治)

[NAIPC2016]Jewel Thief(决策单调性+分治)

题面

原题提交地址(题目编号H)

原题面下载地址

\(n\)个物品,每个物品有一个体积\(w_i\)和价值\(v_i\),现在要求对\(V \in [1,m]\),求出体积为\(V\)
背包能够装下的最大价值

\(1 ≤ n ≤ 1000000; 1 ≤ m ≤ 100000; 1 ≤ w_i ≤ 300; 1 ≤ v_i ≤ 10^9\)

分析

决策单调性发现

注意到物品的体积很小,考虑按体积分类,选取同种体积的物品时,一定优先选择价值大的物品。

\(dp[i][j]\)为使用前i种体积的物品,体积为j的最大价值。类似多重背包的单调队列优化,将模i同余的所有位置拿出来重新标号(即下标看作1,2,3....x)。
则有

\[dp[i][j]=\max_{k=0}^j dp[i-1][k]+val(k,j) \]

其中\(w(k,j)\)表示第\(i\)种体积的物品中,最大的\(j-k\)个的价值和

显然\(val\)满足四边形不等式。

问题转化

\(A\)为一个矩阵,\(pos(j)=max(\{i|,\forall p \in [0,m],A[i][j]>A[p][j] \})\),即使得第\(j\)列第\(i\)行的值最大的\(i\)(如果有多个i相同,则取编号最大的)

在原问题中,考虑第\(i-1\)层到第\(i\)层的转移 ,令\(A[k][j]=\begin{cases} dp[i-1][k]+val(k,j),k \leq j \\ -\infty,k>j\end{cases}\),我们发现dp的转移实际上就是在求第\(k\)行第\(j\)列的最大值,其中\(j\)固定。那么\(dp[i][j]=A[pos(j)][j]\)

于是问题就转化为:已知一个\((m+1)\times(m+1)\)大小的矩阵\(A\),其中每个元素的值均可以在\(O(1)\)时间内查询。现
要求对于\(j \in [0,m]\),求出\(A[pos(j)][j]\)

分治求解

对列[l,r]进行分治,维护当前可能成为\(pos\)的行\([x,y]\),令\(mid=\frac{l+r}{2}\),暴力枚举所有可能的行求出\(pos(mid)\),分治递归操作\([l,mid-1] [x,pos(mid)]\)以及\([mid +1,r] [pos(mid),y]\)直至\(l = r\)\(x = y\)。容易发现这样的时间复杂度是\(O(m\log m)\)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define maxw 300
#define maxn 1000000
using namespace std;
typedef long long ll;
vector<ll>w[maxw+5];
ll dp[2][maxn+5];
int n,m;
void divide(int l,int r,int x,int y,int now,int mod,int rest){
	//列[l,r],行[x,y]; 
	if(l>r) return;
	int mid=(l+r)>>1,pos=mid;
	dp[now^1][mid*mod+rest]=dp[now][mid*mod+rest];
	for(int j=min(y,mid-1);j>=x;j--){//枚举可能成为pos(mid)的列,注意j<mid 
		if(mid-j>(int)w[mod].size()) break;
		if(dp[now][j*mod+rest]+w[mod][mid-j-1]>dp[now^1][mid*mod+rest]){
			dp[now^1][mid*mod+rest]=dp[now][j*mod+rest]+w[mod][mid-j-1];
			pos=j;
		} 
	}
	divide(l,mid-1,x,pos,now,mod,rest);
	divide(mid+1,r,pos,y,now,mod,rest);
}

inline int cmp(int x,int y){
	return x>y;
}
int main(){
	int x,y;
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d %d",&x,&y);
		w[x].push_back(y);
	} 
	for(int i=1;i<=maxw;i++){
		sort(w[i].begin(),w[i].end(),cmp);
		for(int j=1;j<(int)w[i].size();j++) w[i][j]+=w[i][j-1]; 
	}
	int now=0;
	for(int i=1;i<=300;i++){
		if(w[i].size()){
			for(int j=0;j<i;j++){
				//将模i同余的所有位置拿出来
				divide(0,(m-j)/i,0,(m-j)/i,now,i,j); 
			}
			for(int j=1;j<=m;j++){
				dp[now^1][j]=max(dp[now^1][j],dp[now^1][j-1]);
				//我们dp的子状态是体积<=j,而分治过程中是=j 
			}
			now^=1;
		}
	}
	for(int i=1;i<=m;i++) printf("%I64d ",dp[now][i]);
}

posted @ 2019-12-18 16:29  birchtree  阅读(810)  评论(0编辑  收藏  举报