赞
踩
原题链接:https://codeforces.com/problemset/problem/1935/C
凯夫特梅鲁姆硕士生援助中心的新信使计划进行更新,开发人员希望在更新中优化显示给用户的信息集。目前共有 n 条信息。每条信息由两个整数 ai 和 bi 表示。读取数字为 p1,p2,…,pk ( 1≤pi≤n ,所有 pi 都是不同的)的信息所花费的时间由公式计算得出:
请注意,读取由**个编号为 p1 的报文组成的报文集所需的时间等于 ap1 。此外,读取一组空信息所需的时间为 0 。
用户可以确定他愿意在信使中花费的时间 l 。信使必须告知用户信息集的最大可能大小,其阅读时间不超过 l 。请注意,信息集的最大大小可以等于 0 。
流行信使的开发人员未能实现这一功能,因此他们要求您解决这一问题。
每个测试由多个测试用例组成。第一行包含一个整数 t ( 1≤t≤5⋅10^4 ) - 测试用例的个数。测试用例说明如下。
每个测试用例的第一行包含两个整数 n 和 l ( 1≤n≤2000 , 1≤l≤10^9 )--信息数量和用户愿意在信使中花费的时间。
接下来 n 行的 i (th)包含两个整数 ai 和 bi 。( 1≤ai,bi≤10^9 )--第 i 行信息的特征。
保证所有测试用例的 n^2 之和不超过 4⋅10^6 。
对于每个测试用例,输出一个整数 - 一组报文的最大可能大小,其读取时间不超过 l 。
Example input 5 5 8 4 3 1 5 2 4 4 3 2 3 1 6 4 10 3 12 4 8 2 1 2 12 5 26 24 7 8 28 30 22 3 8 17 17 5 14 15 3 1000000000 998244353 179 239 228 1337 993 1007 output 3 1 2 1 0
注
在第一个测试用例中,您可以选取由三条信息组成的一组,其编号分别为 p1=3 、 p2=2 和 p3=5 。读取这组信息所花费的时间等于 a3+a2+a5+|b3−b2|+|b2−b5|=2+1+2+|4−5|+|5−3|=8
在第二个测试用例中,您可以获取一组编号为 p1=1 的信息。读取这组信息所花费的时间等于 a1=4 。
在第五个测试案例中,可以证明不存在这样一个非空的信息集合,其阅读时间不超过 l 。
一眼看上去像背包,但是分析一下发现直接背包时间复杂度非常大,实际上这个题目可以进行常规的线性dp,首先发现代价l非常大,那么代价肯定不能放进状态里面,我们需要求的是最多可以选择多少个位置,并且总代价不超过l,这里涉及到了位置的数量,我们可以尝试把选择的元素个数放进状态,定义f[i][j]表示到了第i个位置,并且已经选择了j个元素的最小代价,但是代价貌似不好计算啊,ai直接把每一个加上就可以了,关键难处理的地方就是bi的代价怎么处理呢,如果我们选择了b中的k个元素,分别为[b1,b2,b3,...,bk],实际上这里我们要处理的是怎么排列b的顺序使得b的代价最低,证明发现,b按照从小到大的顺序排列时b的总代价最小,下面来证明一下:
首先如果按照从小到大的顺序排列为[b1,b2,b3,...,bk],代价就是|b1-b2|+|b2-b3|+...+|b(k-1)-bk|
可以把绝对值去掉就是b2-b1+b3-b2+...+bk-b(k-1),那么中间的项会全部消掉,只留下b1和bk
也就是代价等于bk-b1,如果随意打乱顺序,那么首先bk-b1一定会留下,并且中间一定还会留下一些正的贡献,也就是会使得代价变大,所以b按照从小到大排序时,代价最小。
所以首先我们对于每个位置按照bi从小到大排序,然后考虑dp即可
定义f[i][j]表示到了第i个位置并且选择了j个元素最小代价,a的代价就是所有ai的和,b的代价就是max(bi)-min(bi) (i属于s),s是我们选中的元素的集合。
初始化:
f[i][1]=c[i].a-c[i].b
if f[i][1]+c[i].b<=l,说明可以只选中i这一个位置
g[i][1]=min(g[i-1][1],f[i][1])
状态转移:
f[i][j]=min(MAX,g[i-1][j-1]+c[i].a); //选择当前位置,这里和MAX取min可以防止爆int,就可以不使用longlong了,因为如果f[i][j]超过MAX和置为MAX不影响结果,因为总代价l<MAX,所以超出l的部分是没有意义的。
if(f[i][j]+c[i].b<=l)ans=j; //说明可以在代价小于等于l的情况下选择j个位置,更新答案
g[i][j]=min(g[i-1][j],f[i][j]); //g[i-1][j]表示不选择当前位置
最终答案:
最终答案就是满足f[i][j]+c[i].b<=j的最大的j。 //初始化的时候已经减去选中的第一个元素的bi,这里还需要加上当前位置的bi。
时间复杂度:O(n^2)。
空间复杂度:O(n^2)。
cpp代码如下:
- #include <cstdio>
- #include <cstring>
- #include <iostream>
- #include <algorithm>
-
- using namespace std;
- typedef long long LL;
- typedef pair<int,int>PII;
-
- const int N=2010,MAX=1e9+10;
-
- int T,n,l;
- struct Node {
- int a,b;
- }c[N];
- int f[N][N],g[N][N];
-
- void solve()
- {
- cin>>n>>l;
- for(int i=1;i<=n;i++)cin>>c[i].a,cin>>c[i].b;
- sort(c+1,c+1+n,[&](Node A,Node B){ //按照bi从小到大排序
- return A.b<B.b;
- });
-
- for(int i=0;i<=n;i++)
- for(int j=0;j<=n;j++)
- f[i][j]=g[i][j]=MAX;
-
- int ans=0;
- for(int i=1;i<=n;i++) //初始化
- {
- f[i][1]=c[i].a-c[i].b; //先减去选择的第一个位置的b,直接放在特定状态的初始化里面,方便后续处理
- if(f[i][1]+c[i].b<=l)ans=1;//说明可以在代价小于等于1的情况下选择j个位置,更新答案
- g[i][1]=min(g[i-1][1],f[i][1]);
- }
-
- for(int j=2;j<=n;j++)
- for(int i=j;i<=n;i++)
- {
- f[i][j]=min(MAX,g[i-1][j-1]+c[i].a); //选择当前位置,这里和MAX取min可以防止爆int,就可以不使用longlong了
- if(f[i][j]+c[i].b<=l)ans=j; //说明可以在代价小于等于L的情况下选择j个位置,更新答案
- g[i][j]=min(g[i-1][j],f[i][j]); //g[i-1][j]表示不选择当前位置
- }
- cout<<ans<<'\n';
- }
- int main()
- {
- ios::sync_with_stdio(false);
- cin.tie(0),cout.tie(0);
- cin>>T;
- while(T--)
- {
- solve();
- }
- return 0;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。