题意:有一个大小为n的整数数组a。最初,数组所有元素都为1.
可以执行如下操作:选定一个i和任意的x(x>0)然后把ai=ai+[ai/x](向下取整)
执行所有操作后,若ai==bi,那么你可以得到ci个金币。通过执行不超过k次的操作使得获得最大的金币数量。
1->bi的最小操作数,用广搜得到。所以用广搜预处理1->1000的所有最小操作数,然后来一遍01背包即可。
细节处理:由于k太大了,但是可以发现任意一个1->bi的操作数最多为20左右,所以最多操作数总和也不过2e4左右,所以在跑背包之前先判断一下k与总操作数的大小关系;
#include <bits/stdc++.h>
using namespace std;
const int N=1005;
int n,k;
int v[N];
int b[N];
int w[N];
int cost[N];
bool flag[N];
void solve(){
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>b[i];
for(int i=1;i<=n;i++) cin>>w[i];
int sum=0;
for(int i=1;i<=n;i++){
v[i]=cost[b[i]];
sum+=v[i];
}
int ans=0;
if(sum<=k){
for(int i=1;i<=n;i++) ans+=w[i];
cout<<ans<<'\n';
}else{
static int f[1000005];
memset(f,0,sizeof(f));
for(int i=1;i<=n;i++){
for(int j=k;j>=v[i];j--){
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
cout<<f[k]<<'\n';
}
}
void bfs(){
queue<int> que;
que.push(1);
flag[1]=true;
while(que.size()){
int u=que.front();
que.pop();
for(int i=1;i<=u;i++){
int t=u+u/i;
if(t>=N) continue;
if(flag[t]) continue;
que.push(t);
cost[t]=cost[u]+1;
flag[t]=true;
}
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
bfs();
int t;cin>>t;
while(t--) solve();
return 0;
}