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}\)
$ 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|)\)
接下來考慮狀態轉移方程:
方案 \(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;
}