1. 程式人生 > 實用技巧 >BZOJ-1237 [SCOI2008]配對(dp)

BZOJ-1237 [SCOI2008]配對(dp)

題目描述

  給一個長為 \(n\) 的序列 \(a=[a_1,a_2,\cdots,a_n]\) 和一個長為 \(n\) 的序列 \(b=[b_1,b_2,\cdots,b_n]\)(保證所有 \(a_i\) 互不相同,所有 \(b_i\) 互不相同),把它們配對,即每個 \(a_i\) 對應一個 \(b_i\),要求所有配對的整數差的絕對值之和儘量小,但不允許兩個相同的數配對。如果無法配對,輸出 \(-1\)

  資料範圍:\(1\leq n\leq 10^5,1\leq a_i,b_i\leq 10^6\)

分析

  假設不考慮不允許相同的數配對的限制,則把兩個序列排序後對應項一一匹配就是最優答案。

  現在考慮限制條件,且只考慮前 \(i\) 對數,由於保證 $a_i $ 互不相同, \(b_i\) 互不相同,所以排序後如果有 \(a_i=b_i\),則一定有 \(a_i\neq b_{i-1},a_{i}\neq b_{i-2},b_i\neq a_{i-1},b_i\neq a_{i-2}\)。有如下匹配方案:

  \(0.\) \(a_{i}\) 匹配 \(b_{i}\)\(a_i\neq b_i\))。

  $ 1.$ \(a_{i-1}\) 匹配 \(b_i\)\(a_i\) 匹配 \(b_{i-1}\)

  $ 2.$ \(a_{i}\) 匹配 \(b_{i-2}\)\(a_{i-1}\)

匹配 \(b_{i-1}\)\(a_{i-2}\) 匹配 \(b_{i}\)

  $ 3.$ $a_{i} $ 匹配 \(b_{i-1}\),$a_{i-1} $ 匹配 $b_{i-2} \(,\)a_{i-2} $ 匹配 \(b_i\)

  \(4.\) \(a_i\) 匹配 \(b_{i-2}\)\(a_{i-1}\) 匹配 \(b_i\)\(a_{i-2}\) 匹配 \(b_{i-1}\)

  因此可以歸納出一個結論:每個數 \(a\) 與其匹配的數 \(b\) 下標之差不會超過 \(2\)

  設 \(dp[i]\) 表示前 \(i\) 對數配對能得到的最小值。\(dp[1]=|a_1-b_1|,dp[2]=\min(dp[1]+|a_2-b_2|,|a_1-b_2|+|a_2-b_1|)\)

(如果 \(n=1\)\(a_1=b_1\) 將其特判掉)。

  接下來考慮狀態轉移方程:

  方案 \(0\)\(dp[i]=dp[i-1]+|a_i-b_i|\)

  方案 \(1\)\(dp[i]=\min(dp[i],dp[i-2]+|a_i-b_{i-1}|+|a_{i-1}-b_i|)\)

  方案 \(2\):$dp[i]=\min(dp[i],dp[i-3]+|a_i-b_{i-2}|+|a_{i-1}-b_{i-1}|+|a_{i-2}-b_i|) $。

  方案 \(3\)\(dp[i]=\min(dp[i],dp[i-3]+|a_i-b_{i-1}|+|a_{i-1}-b_{i-2}|+|a_{i-2}-b_i|)\)

  方案 \(4\)\(dp[i]=\min(dp[i],dp[i-3]+|a_{i}-b_{i-2}|+|a_{i-1}-b_{i}|+|a_{i-2}-b_{i-1}|)\)

  \(dp[n]\) 即為答案,時間複雜度 \(O(n)\)

程式碼

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
const long long INF=1e18;
long long n,dp[N],a[N],b[N];
long long ABS(long long a,long long b)
{
    if(a==b)
        return INF;
    return abs(a-b);
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        scanf("%d %d",&a[i],&b[i]);
    sort(a+1,a+1+n);
    sort(b+1,b+1+n);
    if(n==1&&a[1]==b[1])
    {
        puts("-1");
        return 0;
    }
    dp[1]=ABS(a[1],b[1]);
    dp[2]=min(dp[1]+ABS(a[2],b[2]),ABS(a[1],b[2])+ABS(a[2],b[1]));
    for(int i=3;i<=n;i++)
    {
        dp[i]=dp[i-1]+ABS(a[i],b[i]);
        dp[i]=min(dp[i],dp[i-2]+ABS(a[i],b[i-1])+ABS(a[i-1],b[i]));
        dp[i]=min(dp[i],dp[i-3]+ABS(a[i],b[i-2])+ABS(a[i-1],b[i-1])+ABS(a[i-2],b[i]));
        dp[i]=min(dp[i],dp[i-3]+ABS(a[i],b[i-1])+ABS(a[i-1],b[i-2])+ABS(a[i-2],b[i]));
        dp[i]=min(dp[i],dp[i-3]+ABS(a[i],b[i-2])+ABS(a[i-1],b[i])+ABS(a[i-2],b[i-1]));
    }
    cout<<dp[n]<<endl;
    return 0;
}