顯然遇到這種dp題我們要先打暴力,通過讀題我們可以快速推出dp式子:
狀態:設\(dp[i]\)為前i個人的最大價值
狀態轉移方程:
設\((\sum_^i a[k]) = x\)
\(dp[i] = max(dp[j] + a * x^2 + b * x + c)\)
累加我們可以直接字首和優化,設\((\sum_^i a[k]) = sum_i\)
那麼轉移方程即可化為:\(dp[i] = max(dp[j] + a * (sum_i - sum_j)^2 + b *(sum_i - sum_j) + c)\)
至此如果你不像作者一樣sb把初始化搞錯,那麼你已經有50分的高分
暴力**:
#include #include using namespace std;
const int maxn = 1000005;
long long n, a, b, c, x[maxn], sum[maxn], dp[maxn];
int main()
for (int i = 1; i <= n; i++)
}printf("%lld", dp[n]);
}
通過觀察式子,發現帶有平方以及二次項,並且不定的只有個j,再根據標籤我們斷定這道題要用斜率優化
那麼我們開始推式子(這裡已將同類項抵消):
設乙個j,k且j比k更優:
\(dp_j + a * sum_j^2 - b * sum_j - 2 * a * sum_i * sum_j > dp_k + a * sum_k^2 - b * sum_k - 2 * a * sum_i * sum_k\)
\(dp_j + a * sum_j^2 - b * sum_j - (dp_k + a * sum_k^2 - b * sum_k) > 2 * a * sum_i * (sum_j - sum_k)\)
若j在k右邊則:
若j在k左邊則:
得到兩結論:
直接可以排除掉下凸殼情況,用單調佇列維護上凸殼即可。
#include #include using namespace std;
const int maxn = 1000005;
long long n, a, b, c, x[maxn], sum[maxn], dp[maxn], q[maxn];
long long get_dp(long long i, long long j)
long long get_up(long long i, long long j)
long long get_down(long long i, long long j)
int main()
int head = 0, end = 0;
for (int i = 1; i <= n; i++)
printf("%lld", dp[n]);
}
最後提一下,這道題有維護下凸殼的做法,就是將a直接移到左邊即:
但是這樣因為\(a < 0\), 所以不等號方向改變,結論也隨之逆轉(我就因為打這個調了半天),進而要維護下凸。比較難調,以並不推薦這種做法。
特別行動隊題解
刷水題什麼的最愉快了。題意十分明了,就是選出一種分配方案將士兵分為若干組,使修正後的戰鬥力最大。我們先可以寫出暴力dp轉移 設 f n 為將前 i 個士兵分組,且第 i 個士兵為最後一組最後乙個的最大戰鬥力。f i max x j 則化為 f i max tmp,q n inline int rea...
特別行動隊 斜率優化
apio2010特別行動隊 令s為字首和,那麼n方dp f i max 展開,移項得到 f j a s j s j 2 a s i b s j f i a s i s i b s i c。即以f j a s j s j 為y,s j 為x的一次函式,用斜率優化。因為斜率單調遞減,所以維護乙個單調遞減...
APIO2010 特別行動隊
你有一支由 n 名預備役士兵組成的部隊,士兵從 1 到 n 編號,要將他們拆分 成若干特別行動隊調入戰場。出於默契的考慮,同一支特別行動隊中隊員的編號 應該連續,即為形如 i,i 1,i k i,i 1,i k 的序列。編號為 i 的士兵的初始戰鬥力為 xi 一支特別行動隊的初始戰鬥力 x 為隊內 ...