传送门
本题主要就是让选
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 3t⋅10x,如果将 k k k个数字的第 x x x位视为一个类型,并且不考虑那些无法被 3 3 3整除的背包容量,那么选择分配权值的效果等价于从 3 k 3k 3k个大小为 3 ⋅ 1 0 x 3\cdot 10^x 3⋅10x,权值为 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 c⋅10x,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(k−1)个前文所述的物品(权值为 3 ⋅ 1 0 x 3\cdot 10^x 3⋅10x)。注意到如果在同一位上有两个数字的值是 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(k−1)个对应类型的物品,那么每一位上都空出了一个数字来给予其它的选择,这样的话这个数字在这一位上可能是 0 ∼ 9 0\sim9 0∼9的所有数,因此枚举所有可能即可,也就是说,我们对第 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 c⋅10x,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]);
}
}