[SCOI2008]配对 (贪心,动态规划)

Posted kv-stalin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[SCOI2008]配对 (贪心,动态规划)相关的知识,希望对你有一定的参考价值。


题目链接

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]);
则有以下几种情况:

  1. (a[i-2],a[i],a[i-1])
  2. (a[i-1],a[i-2],a[i])
  3. (a[i-1],a[i],a[i-2])
  4. (a[i],a[i-2],a[i-1])
  5. (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]);
}

以上是关于[SCOI2008]配对 (贪心,动态规划)的主要内容,如果未能解决你的问题,请参考以下文章

[SCOI2008]配对

luogu P2507 [SCOI2008]配对

BZOJ1786 [Ahoi2008]Pair 配对 动态规划 逆序对

1237: [SCOI2008]配对

[SCOI2008]配对

Bzoj1237 [SCOI2008]配对