把a,b混在一起排序,常规操作~~a标记-1,b标记1.
嗯,首先我们可以考虑两个方向,一个是仿照最大流求,最值,一个是dp,因为像这种求数值的题目,方向就是这样,还有其他方向,欢迎留言。
然后注意两个性质:
1:如果一个区间内a,b数目都相同,那么这个区间内的a是不会和这个区间之外的b进行匹配的。
2:言下之意就是我们要确定从那个位置开始确定区间,因为同一个位置的a可能在多个能够满足上述区间里,也就是说,我们必须要在这些区间里选一个。怎样选,就靠dp拉。
3.还有一个要注意的要点是如果我们找到了一个区间,要去求这个区间里的最值,你当然不能暴力求解了,注意到性质1,如果我们把这个区间的两边的两个数去掉后就是个子区间,这个子区间的值通过我们前面求的值是能求出的,用一些最值减去就好了,为什么能减掉?因为要满足最值,你可以用反证法试试,而且你一定注意性质1.,所以我们可以看出在我们要求的区间里,这些子区间是一整块,一整块的,它们并没有和区间外的任何数值相连.(这是线性dp较难分析的部分,不过dp的关键不就是用以前求的来更新现在么。)
具体做法就是先把a,b混在一起排序,然后把a标记为1,b标记为-1,如果某段和为0那么这段里的a,b一定要相互匹配才能有最大值。
因为段与段之间会重合,这就涉及到某个段是选还是不选的问题所以我们要dp,目的就是选出其中的段不相互重合的最大值。
求出dp[i]表示匹配了i个的最值。
another[i]表示在位置i之前尽量匹配完的最值。也就是说如果3个a,6个b那么就只能配3个,而不是4个或者2个
tempdp[i]表示区间位置一定是从i开始的最值,它和another[i]的不同就是another[i]求出的最值,开始的那个区间不一定是从i开始的
pre[i],前面有相同前缀和的数出现的位置。主要是为了选出和为0的段而设计的。
注意pre【】这个数组一定要开大点啊!!我被坑惨了!!400000左右吧,否则错得很离谱~~
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
struct numm
{
int k;
ll v;
};
bool com(numm a, numm b)
{
return a.v < b.v;
}
numm num[250000];
int n, m, all, t;
ll dp[120000];
ll tempdp[250000],another[250000];
int posnum1,posnum2,pre[500000];
int main()
{
scanf("%d", &t);
while (t--)
{
scanf("%d%d", &n, &m);
all = n + m; posnum1 = 0; posnum2 = 0;
for (int i = 1; i <= n; i++)
{
scanf("%d", &num[i].v);
num[i].k = 1;
}
for (int i = n + 1; i <= all; i++)
{
scanf("%d", &num[i].v);
num[i].k = -1;
}
sort(num + 1, num + all + 1, com);
int sum = 0;
for (int i = 0; i <=2 * all; i++)
tempdp[i] = 0, pre[i] = -1,another[i]=0;
pre[all] = 0;
for (int i = 0; i <= m; i++)
dp[i] = 2000000000000000;
dp[0] = 0;
for (int i = 1; i <= all; i++)
{
sum += num[i].k;
int temppsum = sum + all;
if (num[i].k < 0)
posnum1++;
else
posnum2++;
if (pre[temppsum] == -1)
{
tempdp[i] = dp[min(posnum1,posnum2)];
}
else
{
int realpre = pre[temppsum] + 1;
if ((i - realpre) == 1)
{
tempdp[i] = another[realpre - 1] + abs(num[i].v - num[realpre].v);
}
else
{
tempdp[i] = tempdp[i - 1] - another[realpre] + abs(num[i].v - num[realpre].v) + another[realpre - 1];
}
}
int r = min(posnum1, posnum2);
dp[r] = min(dp[r], tempdp[i]);
another[i] = dp[r];
pre[temppsum] = i;
}
printf("%lld\n", dp[m]);
}
}