1. 程式人生 > >[BZOJ1237][SCOI2008]配對(貪心+dp)

[BZOJ1237][SCOI2008]配對(貪心+dp)

傳送門 題意:n 個整數A[i]和n個整數B[i]。把它們配對,要求所有配對的整數差的絕對值之和最小,不允許兩個相同的數配對。(n<=100000)

首先排序兩個陣列。然後我們考慮一下簡單的問題:如果兩個相同的數允許配對的話,我們排完序之後每一位的一一對應配對就行了。但是現在不允許相同的數配對,那麼我們就需要儘量交換相近的數使得我們交換所產生的代價儘量的小。但是如果相等的數很多會複雜度退化,怎麼辦呢?我們從少到多列一下交換的情況,先列一下2個(1個交叉)和3個(2個交叉)的 在這裡插入圖片描述 我們都假設極端情況: (1)中設a=c, b=d 那麼我們需要a連d,b連d (2)(3)中設a=d,b=d,c=f,那麼我們需要a連f,b連d,c連e(2情況)或者反過來(3情況),那麼我們不論如何交換,都無法把(2)(3)中的兩個交叉減少到(1)這樣的一個交叉。 做到這一步我們是絕望的,但是不要灰心,我們繼續往前列。 我們列一下4個(3個交叉)的情況,同樣設上下相等,不抱希望的嘗試交換減少交叉,但是!這樣是可以減少交叉的! 在這裡插入圖片描述

我們只需要交換下面的g和h就可以減少一個交叉。那麼興奮地列一些大於2的交叉的情況,發現都可以通過類似的方法優化到2個交叉。那麼也就是說:將兩個數列排序後,每個位置與他配對的位置的距離不會超過2!

那麼我們就可以根這個來dp。設F[i]表示現在匹配到i,所有配對的整數差的絕對值之和最小是多少。那麼我們就能繼承i-1,i-2,和i-3的狀態,分別對應:上下直接匹配(我沒列出來,直接相連沒有交叉),(1)情況,(2)和(3)情況(注意都要考慮)。

#include<cstdio>
#include<iostream>
#include<cstring>
#include
<algorithm>
using namespace std; typedef long long ll; const int N=1e5+10; const ll INF=1e13; int a[N],b[N]; ll f[N]; ll get(int x,int y) { if(a[x]==b[y]) return INF; else return abs(a[x]-b[y]); } int main() { int n;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]+get(i,i)); if(i>=2) t=min(t,f[i-2]+get(i,i-1)+get(i-1,i)); if(i>=3) { t=min(t,f[i-3]+get(i,i-2)+get(i-1,i)+get(i-2,i-1)); t=min(t,f[i-3]+get(i,i-1)+get(i-1,i-2)+get(i-2,i)); } f[i]=t; } printf("%lld\n",f[n]); return 0; }