有一堆商品,每个价值为(v_i),代价为(c_i)。收益定义为价值减代价。
你要买一个商品,但是有个小鬼会捣乱,他有(k)次机会使得你正在买的商品的价值变为(0),这时候你不得不重买一遍。
你和小鬼都聪明绝顶,求最终你的收益。
(nle 1.5*10^5,kle 9)
考虑最终的选择策略,即一个选择商品的序列。你按照这个序列选,使得小鬼怎么搞最终的答案都不会更劣。
假如已经知道了这个序列的组成,现在要排序:假如相邻的两个商品(a)和(b),(a)在前(b)在后比反过来优,那么有:(min(v_b-c_b-v_a,0)+v_a-c_a>min(v_a-c_a-v_b,0)+v_b-c_b)。拆开来分类讨论一下,发现这等价于(v_a<v_b)。
我没有把式子拆完整发现这个东西有传递性而一开始不会证QAQ。
先排序,于是就可以DP了。由于正着DP需要记(sum c_i)比较难受,所以可以倒着DP,每次枚举这一位选或不选,看看小鬼是否在这一位搞,即(f_{i,j})表示搞完([i,n]),小鬼搞了(j)次的答案。
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 150005
#define INF 1000000000
int n,k;
struct item{
int v,c;
} a[N];
bool cmp(item a,item b){
// return min(b.w-a.v,0)+a.w>min(a.w-b.v,0)+b.w;
return a.v<b.v;
}
int f[N][10];
int main(){
freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int T;
scanf("%d",&T);
while (T--){
scanf("%d%d",&n,&k);
for (int i=1;i<=n;++i)
scanf("%d%d",&a[i].v,&a[i].c);
if (n<=k){
printf("0
");
continue;
}
sort(a+1,a+n+1,cmp);
for (int j=1;j<=k;++j)
f[n+1][j]=0;
f[n+1][0]=0;
for (int i=n;i>=1;--i){
f[i][0]=max(f[i+1][0],a[i].v-a[i].c);
for (int j=1;j<=k;++j)
f[i][j]=max(f[i+1][j],min(a[i].v-a[i].c,f[i+1][j-1]-a[i].c));
}
// for (int i=1;i<=n;++i,printf("
")){
// for (int j=0;j<=k;++j)
// printf("%d ",f[i][j]);
// }
printf("%d
",f[1][k]);
}
return 0;
}