题目链接
Solution
很妙的DP,很妙的贪心.
首先考虑,如果说没有那个相同的不能配对的情况;
那么我们肯定是直接排两遍序,然后一一对应即可.
但是是有限制的,同时我们可得几个条件供贪心:
-
每个数字仅在 (a) 或 (b) 中出现一次. 即每个序列排序之后满足 (a_i≠b_i).
-
如果 (a_i=b_i) ,我们需要去和其他位置的元素交换;
-
我们交换的元素与当前元素的绝对距离不会大于 (2),也就是说每次我们碰到相同的情况,只需要 (a_i) 与 (a_{i+1}) 或者 (a_{i-1}) 交换.
然后我们定义 (f[i]) 为到第 (i) 个点的时候最小的差值.
考虑3个一组转移,至于为什么是3个,可以看上面的贪心条件.
令原排列为 (a[i-2],a[i-1],a[i]);
则有以下几种情况:
- (a[i-2],a[i],a[i-1])
- (a[i-1],a[i-2],a[i])
- (a[i-1],a[i],a[i-2])
- (a[i],a[i-2],a[i-1])
- (a[i],a[i-1],a[i-2])
然后我们每次通过讨论从 (f[i-3]) 转移过来即可.
注意要预先处理 (f[1],f[2],f[3]) 的值.
Code
#include<bits/stdc++.h>
#define maxn 100005
#define ll long long
using namespace std;
const ll Inf=19260817;
ll f[maxn],n,a[maxn],b[maxn];
ll cal(int x,int y)
{
if (a[x]==b[y]) return Inf;
return abs(a[x]-b[y]);
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d%d",&a[i],&b[i]);
sort(a+1,a+n+1);
sort(b+1,b+n+1);
for (int i=1;i<=n;i++) f[i]=Inf;
f[0]=0;
for (int i=1;i<=n;i++)
{
ll t=inf;
if (i>=1) t=min(t,f[i-1]+cal(i,i));
if (i>=2) t=min(t,f[i-2]+cal(i,i-1)+cal(i-1,i));
if (i>=3) t=min(t,f[i-3]+cal(i,i-1)+cal(i-1,i-2)+cal(i-2,i)),
t=min(t,f[i-3]+cal(i-2,i-1)+cal(i-1,i)+cal(i,i-2));
f[i]=t;
}
printf("%lld
",f[n]);
}