1. 程式人生 > 實用技巧 >E - Two Matching | 思維 + DP

E - Two Matching | 思維 + DP

題目連結:https://ac.nowcoder.com/acm/contest/5668/E

題目大意:

想法:

我們先考慮最小值:

我們假設排序後的下標的序列是:1、2、3、4、5、6

那麼很簡單我們得到的最小值就是 [(2 - 1) + (4 - 3) + (6 - 5)]

我們再來考慮次小值:

由於我們一句確定了最小值,所以對於次小值有很大的限制

以上的證明來自:https://blog.nowcoder.net/n/6facdc43054c4f098b42b2b584de217e?f=comment

我們將兩個解相加得到答案:

(2 - 1) + (4 - 3) + (6 - 5) + (3 - 2) + (5 - 4) + (6 - 1) = 2 * (6 - 1)

1、當長度沒有達到 6 的時候

對於長度為 4 的時候:

最終的結果很容易得到: 2 * (4 - 1)

2、長度為 8 的時候

我們可以考慮兩個方案,轉化成兩個長度為 4 的 或者 直接一個長度為 8 的

長度為四的我們用類似證明裡面的方法可以發現: (1234 和 5678 這種策略是最優秀的)

我們再用這種優秀的策略去和直接選擇一個長度為 8 的對比,發現選擇兩個長度為 4 的是最優的

3、推廣一下,當長度更長的時候

當長度 >= 10 的時候,我們可以考慮把這個串分成多個 長度為 4 和 長度為 6 的串

那麼我們就可以得到 dp 的遞推公式: dp[i] = min(dp[i - 4] + v[i - 1] - v[i - 4] , dp[i - 6] + v[i - 1] - v[i - 6])

我們對於 dp 進行初始化的時候必須要初始化 dp[4] dp[6] dp[8] 【防止 8 的時候被分成有長度為 2 的】

#include <algorithm>
#include <string>
#include <cstring>
#include <vector>
#include <map>
#include <stack>
#include <set>
#include <queue>
#include <cmath>
#include <cstdio>
#include 
<iomanip> #include <ctime> #include <bitset> #include <cmath> #include <sstream> #include <iostream> #include <unordered_map> #define ll long long #define ull unsigned long long #define ls nod<<1 #define rs (nod<<1)+1 #define pii pair<int,int> #define mp make_pair #define pb push_back #define INF 0x3f3f3f3f #define max(a, b) (a>b?a:b) #define min(a, b) (a<b?a:b) const double eps = 1e-8; const int maxn = 2e5 + 10; const ll MOD = 1e9 + 7; const int mlog=20; int sgn(double a) { return a < -eps ? -1 : a < eps ? 0 : 1; } using namespace std; ll f[maxn]; int main() { ios::sync_with_stdio(false); int t; cin >> t; while (t--) { int n; cin >> n; vector<ll> v; for (int i = 0;i < n;i++) { ll x; cin >> x; v.pb(x); } sort(v.begin(),v.end()); f[0] = 0; f[4] = v[3] - v[0]; f[6] = v[5] - v[0]; f[8] = v[7] - v[4] + f[4]; for (int i = 10;i <= n;i += 2) f[i] = min(f[i - 4] + v[i - 1] - v[i - 4],f[i - 6] + v[i - 1] - v[i - 6]); cout << f[n] * 2 << endl; } return 0; }