[BJOI2019]排兵布阵
题目
解说
蒟蒻刷\(DP\)题的时候刷到的好题。
首先这道题很显然是一个背包,每个城堡应该被看作物品,但主要问题就在于其消耗和价值分别是什么?经过一番思考我们发现每个城堡还需要考虑不同敌人的影响,而把城堡看作一个物品就没办法考虑上这一点,怎么办?换个思路,为何不把一个背包看作一组物品呢?
由于我们每一轮攻击中投入士兵的分配都是不变的,所以在每城堡投入一定的兵力时\(s\)轮下来的累计收入是可以预处理的,这样的话我们不妨把投入兵力看作消耗,累计收入看作价值,并且不难发现每个城堡的各个物品间是不能同时选择的(毕竟一个城堡只能选择一个特定的兵力投入),正好形成了一个非常裸的分组背包模型!
时间复杂度方面,预处理为\(O(ns)\)的,跑分组背包时理论上为\(O(nsm)\),极限数据时似乎达到了\(10^8\)水平,但事实上由于常数挺小的而且循环背包容量时可以不用跑满,所以还是可以接受的。
具体一些细节见代码
代码
#include<bits/stdc++.h>
#define re register
using namespace std;
int s,n,m,num[100+3][100+3],f[20000+3],ans;
vector<int> city[100+3];
struct thing{
int cost,val;
};
vector<thing> group[100+3];
int main(){
scanf("%d%d%d",&s,&n,&m);
for(re int i=1;i<=s;i++){
for(re int j=1;j<=n;j++){
scanf("%d",&num[i][j]);
num[i][j]=num[i][j]*2+1;
}
}
for(re int i=1;i<=n;i++){
for(re int j=1;j<=s;j++) city[i].push_back(num[j][i]);
sort(city[i].begin(),city[i].end());
}
for(re int i=1;i<=n;i++)
for(re int j=0;j<city[i].size();j++)
group[i].push_back((thing){city[i][j],i*(j+1)});
for(re int g=1;g<=n;g++){
for(re int j=m;j>=0;j--){
for(re int i=0;i<group[g].size();i++){
if(j>=group[g][i].cost) f[j]=max(f[j],f[j-group[g][i].cost]+group[g][i].val);
else break;
}
}
}
printf("%d\n",f[m]);
return 0;
}
幸甚至哉,歌以咏志。
签名:
我将轻轻叹息,叙述这一切,
许多许多年以后:
林子里有两条路,我——
选择了行人稀少的那一条,
它改变了我的一生。