acm-(多重背包)Codeforces Raif Round 1 (Div. 1 + Div. 2) G.Lucky Numbers

题面
传送门
本题主要就是让选 k k k个数字,加起来等于 n n n(询问),使得 k k k个数字各位上的权值和最大。

先看看权值的定义,对于第 x x x位而言,若数字为 3 t ( t = 0 , 1 , 2 , 3 ) 3t(t=0,1,2,3) 3t(t=0,1,2,3),则权值为 t F x tF_x tFx,且一旦分配,对应总和就会加上 3 t ⋅ 1 0 x 3t\cdot 10^x 3t10x,如果将 k k k个数字的第 x x x位视为一个类型,并且不考虑那些无法被 3 3 3整除的背包容量,那么选择分配权值的效果等价于 3 k 3k 3k个大小为 3 ⋅ 1 0 x 3\cdot 10^x 310x,权值为 F x F_x Fx的物品中选择一些物品,而 x x x只有 6 6 6种,因此这是一个多重背包,题目相当于问大小为 n n n的背包能选择的物品的最大权值和是多少。

如果不考虑无法被 3 3 3整除的背包容量,那么就是普通的多重背包,套上一个二进制优化的写法如下所示(部分代码):

int x=1;
memset(dp,-0x3f,sizeof(dp));
dp[0]=0;
FOR(i,0,6){//考虑数位第i位
	int cur=k;//这里的k其实是题目所给条件中的k的3倍
	for(register int h=1;h<=cur;h<<=1){//二进制拆分
		ll w=3ll*x*h,v=1ll*f[i]*h;
		cur-=h;
		ROF(j,maxn-1,w)dp[j]=max(dp[j],dp[j-w]+v);//背包dp
	}
	if(cur){
		ll w=3ll*x*cur,v=1ll*f[i]*cur;
		ROF(j,maxn-1,w)dp[j]=max(dp[j],dp[j-w]+v);
	}
	x*=10;
}

不过本题还要考虑那些无法被 3 3 3整除的数字,注意到每个数位上对应的物品类型有 3 k 3k 3k个,如果我们选择一个权值为 c ⋅ 1 0 x , c ≠ 0 , 3 , 6 , 9 c\cdot 10^x,c\ne 0,3,6,9 c10x,c=0,3,6,9的数字来填充 k k k个数字中的某一个的当前数位,那么就会占去 1 1 1个数字,这样其实就能够覆盖到所有的数字了(那些无法被 3 3 3整除的容量也能被 d p dp dp到),于是我们最多只能再选 3 ( k − 1 ) 3(k-1) 3(k1)个前文所述的物品(权值为 3 ⋅ 1 0 x 3\cdot 10^x 310x)。注意到如果在同一位上有两个数字的值是 a a a b b b都无法被3整除,那么 a + b a+b a+b其实可以被 0 , 3 , 6 , 9 0,3,6,9 0,3,6,9拆分开来,得到 0 + d , 3 + d , 6 + d , 9 + d 0+d,3+d,6+d,9+d 0+d,3+d,6+d,9+d之一,显然提供的价值都不比 a , b a,b a,b的选择得到的价值少,因此同一位上最多只有一个数字不是 0 , 3 , 6 , 9 0,3,6,9 0,3,6,9

现在不妨考虑更普遍的情况,我们假设每一位上都只能选择 3 ( k − 1 ) 3(k-1) 3(k1)个对应类型的物品,那么每一位上都空出了一个数字来给予其它的选择,这样的话这个数字在这一位上可能是 0 ∼ 9 0\sim9 09的所有数,因此枚举所有可能即可,也就是说,我们对第 x x x位做完一遍多重背包 d p dp dp后,所有的容量在这一位上其实还剩一个数字没有被选择(也可以理解为这个数字上这一位的值是 0 0 0),那么我们再考虑这个数字这一位上的值是什么(对应 10 10 10种情况),然后再 d p dp dp一遍即可,方程跟前面的 d p dp dp是一样的,不过考虑的大小会更多,也就是 c ⋅ 1 0 x , c = 0 , 1 , 2 , 3 , . . . , 9 c\cdot 10^x,c=0,1,2,3,...,9 c10x,c=0,1,2,3,...,9

下面给出完整代码(不含头文件宏定义):

int k;
ll f[6],dp[maxn];
int main(){
	k=rd();k=3*(k-1); 
	FOR(i,0,6)f[i]=rd();
	int x=1;
	memset(dp,-0x3f,sizeof(dp));
	dp[0]=0;
	FOR(i,0,6){
		int cur=k;
		for(register int h=1;h<=cur;h<<=1){
			ll w=3ll*x*h,v=1ll*f[i]*h;
			cur-=h;
			ROF(j,maxn-1,w)dp[j]=max(dp[j],dp[j-w]+v);
		}
		if(cur){
			ll w=3ll*x*cur,v=1ll*f[i]*cur;
			ROF(j,maxn-1,w)dp[j]=max(dp[j],dp[j-w]+v);
		}
		ROF(j,maxn-1,0){
			FOR(w,0,10){
				ll v=0;
				if(w%3==0)v=1ll*w/3*f[i];
				if(j>=1ll*w*x)dp[j]=max(dp[j],dp[j-1ll*w*x]+v);
			}
		}
		x*=10;
	}
	int q=rd();
	while(q--){
		int n=rd();
		printf("%lld\n",dp[n]);
	}
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值