石子合並(四邊形不等式優化)


題目大意很簡單,和普通的石子合並過程沒有區別,只是花費變成了一個多項式,若連續的任意個石子權值和為x,那么代價變為F(x) = sigma(a[i] * x^i),求將n堆石子合並為一隊的最小花費。

對於暴力的做法,復雜度是O(n^3)的,所以要優化

我們知道當a, b, c, d(a <= b < c <= d)當有cost[a][c] + cost[b][d] <= cost[a][d] + cost[b][c] 時,我們稱其滿足四邊形不等式,設p[i][j]表示當區間[i, j]取最優決策時所選擇的下標,這時可以證明有p[i][j - 1] <= p[i][j] <= p[i + 1][j](花了我好長時間終於證明了),沒事了可以證明下看看,也可以記住這個結論。

這時當按區間dp時,計算區間[i, j]的最優解,只要枚舉[p[i][j - 1], p[i + 1][j]]即可,由於數組p取值為[1, n]且是單調的,所以枚舉的總復雜度為O(n),最后加上區間枚舉的復雜度,總復雜度為O(n^2)

所以對於一般性的題目,需要證明的只有dp量是不是滿足四邊形不等式的,對於這道題就是要證明:

設sum(a, b) = x, sum(b, c) = z, sum(c, d) = y;

有 F(x + z) + F(y + z) <= F(z) + F(x + y + z),即證明:

sigma(a[i] * ( (x + z)^i + (y + z)^i - z^i - (x+y+z)^i )) <= 0,轉化為證明:

(x + z) ^ n  +  (y + z) ^ n  -  z ^ n  -  (x + y + z) ^ n <= 0恆成立。

很明顯這個不等式可以利用數學歸納法加以簡單的證明。

 

 1 #include <map>
 2 #include <set>
 3 #include <stack>
 4 #include <queue>
 5 #include <cmath>
 6 #include <ctime>
 7 #include <vector>
 8 #include <cstdio>
 9 #include <cctype>
10 #include <cstring>
11 #include <cstdlib>
12 #include <iostream>
13 #include <algorithm>
14 using namespace std;
15 #define INF 0x3f3f3f3f
16 #define inf (-((LL)1<<40))
17 #define lson k<<1, L, (L + R)>>1
18 #define rson k<<1|1,  ((L + R)>>1) + 1, R
19 #define mem0(a) memset(a,0,sizeof(a))
20 #define mem1(a) memset(a,-1,sizeof(a))
21 #define mem(a, b) memset(a, b, sizeof(a))
22 #define FIN freopen("in.txt", "r", stdin)
23 #define FOUT freopen("out.txt", "w", stdout)
24 #define rep(i, a, b) for(int i = a; i <= b; i ++)
25 
26 template<class T> T CMP_MIN(T a, T b) { return a < b; }
27 template<class T> T CMP_MAX(T a, T b) { return a > b; }
28 template<class T> T MAX(T a, T b) { return a > b ? a : b; }
29 template<class T> T MIN(T a, T b) { return a < b ? a : b; }
30 template<class T> T GCD(T a, T b) { return b ? GCD(b, a%b) : a; }
31 template<class T> T LCM(T a, T b) { return a / GCD(a,b) * b;    }
32 
33 //typedef __int64 LL;
34 typedef long long LL;
35 const int MAXN = 51000;
36 const int MAXM = 110000;
37 const double eps = 1e-4;
38 //LL MOD = 987654321;
39 
40 int T, n, m, s[1100], a[10];
41 LL p[1100][1100], dp[1100][1100];
42 
43 LL fun(int x) {
44     LL ans = 0, p = 1;
45     rep (i, 0, m) {
46         ans += a[i] * p;
47         p *= x;
48     }
49     return ans;
50 }
51 
52 int main()
53 {
54     //FIN;
55     while(~scanf("%d", &T)) while(T--) {
56         scanf("%d", &n);
57         rep (i, 1, n) scanf("%d", s + i), s[i] += s[i - 1];
58         scanf("%d", &m);
59         rep (i, 0, m) scanf("%d", a + i);
60         mem0(dp); mem0(p);
61         rep (len, 1, n) {
62             rep (i, 1, n - len + 1) {
63                 int j = i + len - 1;
64                 LL cost = fun(s[j] - s[i - 1]);
65                 if(len <= 1) { dp[i][j] = 0; p[i][j] = i; }
66                 else rep (k, p[i][j - 1], p[i + 1][j]) {
67                     if(dp[i][k] + dp[k+1][j] + cost < dp[i][j] || dp[i][j] == 0) {
68                         p[i][j] = k;
69                         dp[i][j] = dp[i][k] + dp[k+1][j] + cost;
70                     }
71                 }
72             }
73         }
74         cout << dp[1][n] << endl;
75     }
76     return 0;
77 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM