第八届“英拿科技杯”上海高校金马程序设计联赛暨东华大学邀请赛
仪表盘所有提交榜单
I. 孤星
单点时限: 2.0 sec
内存限制: 512 MB
𝑛(1≤103) 个干员,每个干员工资为 𝑤𝑖(1≤𝑤𝑖≤105),贡献值为 𝑣𝑖(1≤𝑣𝑖≤102)。
给出 𝑚(1≤𝑚≤105)次询问,每次询问:当你能承受的总工资为 𝑊(0≤𝑊≤109) 时,能收获最大的贡献值是多少。
输入格式
第一行,两个整数 𝑛,𝑚。
接下来 𝑛 行,每行两个整数 𝑤𝑖,𝑣𝑖,表示每个干员的工资和贡献值。
接下来𝑚行,每行一个数 𝑊,表示单次询问中的可以承受的总工资。
输出格式
输出共𝑚行,每行一个数表示你的答案。
样例
input
4 3 1 2 2 1 1 1 2 3 5 1 0output
6 2 0
思路:这个题目真的让我对dp的理解加深了,其实这个就是一个大容量背包的问题,由于容量大,然后价值小,所以就可以考虑把下标和值调换一下,变成求一个价值最大为j的时候的最小的背包的总量dp[j]的问题,这样的话我们进行一次打表,之后的询问就可以O(logn)直接查询。这里要注意的就是我们这里求最小的问题的dp数组并不是一个递增的数组,原因就是因为不是每一个值都能往前倒到0,只有dp【0】为0的时候我们才能进行最小更新,所以我们进行需要进行一个后缀最小值处理,这样的话我们就可以在查询的时候减少时间复杂度。
#include "bits/stdc++.h" using namespace std; const int N = 1e9 + 3; long long w[2000], v[2000], dp[100010]; #define Inf 1000000005 //map<long long, long long> dp; int main(){int n, m;cin>>n>>m;for(int i = 1; i <= n; i ++){cin>>w[i]>>v[i];}fill(dp, dp + 100010, Inf); // memset(dp, 0, sizeof(dp));dp[0] = 0;for(int i = 1; i <= n; i ++){for(int j = 100010; j >= 0; j --){dp[j] = dp[j];if(j - v[i] >= 0){dp[j] = min(dp[j], dp[j - v[i]] + w[i]);}}}long long W; // sort(dp, dp + 100010);for (int i = 100000; i >= 0; i--) dp[i] = min(dp[i], dp[i + 1]);for(int k = 0; k < m; k ++){cin>>W; long long *ans = upper_bound(dp, dp + 100009, W);if(ans - dp == 0) cout<<0<<endl;else cout<<ans - dp - 1<<endl; }return 0; }