题解 P2507 【[SCOI2008]配对】

· · 题解

Solution:

  这个贪心神了。。。

  我们首先考虑没有限制条件,即不需要满足配对时a_i\neq b_i这个条件,那么很容易想到贪心的思路,直接对a,b从小到大排一遍序,然后累加同项之差相减的绝对值即可。

  但是现在有了限制,各种方法骚,没有刚过,感觉完全不可做。

  后面仔细看题,发现一个超级重要的条件,保证a中数各不相同,b中数各不相同。

  那么就好搞了,我们先对a,b各自从小到大排一遍序,然后进行以下操作:

  首先考虑输出-1的情况:很显然只有当序列长度为1a_1=b_1时无解,因为当n\geq 2时,由于数各不相同,那么同一个数a,b中顶多各自出现一次,我们就可以用后面的数和前面的数配对(显然一定不会相等)。

  其次,当有解时,我们贪心的想到,因为每个数顶多在a,b中各出现一次,所以当某次a_i=b_i时,则a_i\neq b_{i-1}a_i\neq b_{i+1}b_i同理),于是我们可以让其和a_{i-1},b_{i-1}或者a_{i+1},b_{i+1}搭配,或者三个互相搭配,在中间取最小值就好了。(即使这三对数,每对相同,但由于a,b中数各自不同,那么这三对数一定可以搭配出两两不同的情况

  那么由于前两次需要判断边界,且i+1可能越界。于是将过程改为a_i,a_{i-1},a_{i-2}三个比较取最小值(都一样的)。

  排序后,求解的整个过程是线性的,于是用DP的思想来转移实现。

  定义状态f[i]表示前i个配对能得到的最小值,因为每次转移是三个比较,则初值f[1]=|a_1-b_1|f[2]=min(f[1]+|a2-b2|,|a1-b2|+|a2-b1|)注意当配对的两个数相等时,将其算出的值赋为inf)。

  那么不难得出状态转移方程:

  f[i]=f[i-1]+|a_i-b_i|(直接配对a_ib_i

  f[i]=min(f[i],f[i-2]+|a_i-b_{i-1}|+|a_{i-1}-b_i|)ii-1配对)

  f[i]=min(f[i],f[i-3]+|a_i-b_{i-2}|+|a_{i-1}-b_{i-1}|+|a_{i-2}-b_i|)(三个配对时,让ii-2配对)

  f[i]=min(f[i],f[i-3]+|a_i-b_{i-2}|+|a_{i-1}-b_i|+|a_{i+1}-b_{i-1}|)(三个配对时,a,b两两匹配的一种情况)

  f[i]=min(f[i],f[i-3]+|a_i-b_{i-1}|+|a_{i-1}-b_{i-2}|+|a_{i+1}-b_i|)(三个配对时,a,b两两匹配的另一种情况)

  最后目标状态f[n]就是答案。

### 代码: ```cpp #include<bits/stdc++.h> #define il inline #define ll long long #define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++) #define Min(a,b) ((a)>(b)?(b):(a)) #define la(a,b) ((a!=b)?((a-b)>0?(a-b):(b-a)):233333333) using namespace std; const int N=100005; ll n,f[N],a[N],b[N]; il int gi(){ int a=0;char x=getchar();bool f=0; while((x<'0'||x>'9')&&x!='-')x=getchar(); if(x=='-')x=getchar(),f=1; while(x>='0'&&x<='9')a=(a<<3)+(a<<1)+x-48,x=getchar(); return f?-a:a; } int main(){ n=gi(); For(i,1,n) a[i]=gi(),b[i]=gi(); sort(a+1,a+n+1);sort(b+1,b+n+1); if(a[1]==b[1]&&n==1){cout<<-1;return 0;} f[1]=la(a[1],b[1]); f[2]=Min(f[1]+la(a[2],b[2]),la(a[1],b[2])+la(a[2],b[1])); For(i,3,n){ f[i]=f[i-1]+la(a[i],b[i]); f[i]=Min(f[i],f[i-2]+la(a[i],b[i-1])+la(a[i-1],b[i])); f[i]=Min(f[i],f[i-3]+la(a[i],b[i-2])+la(a[i-2],b[i])+la(a[i-1],b[i-1])); f[i]=Min(f[i],f[i-3]+la(a[i],b[i-1])+la(a[i-1],b[i-2])+la(a[i-2],b[i])); f[i]=Min(f[i],f[i-3]+la(a[i],b[i-2])+la(a[i-1],b[i])+la(a[i-2],b[i-1])); } cout<<f[n]; return 0; } ```