P2900 [USACO08MAR]土地徵用Land Acquisition
阿新 • • 發佈:2019-01-06
\(\color{#0066ff}{ 題目描述 }\)
約翰準備擴大他的農場,眼前他正在考慮購買N塊長方形的土地。如果約翰單買一塊土 地,價格就是土地的面積。但他可以選擇併購一組土地,併購的價格為這些土地中最大的長 乘以最大的寬。比如約翰併購一塊3 × 5和一塊5 × 3的土地,他只需要支付5 × 5 = 25元, 比單買合算。 約翰希望買下所有的土地。他發現,將這些土地分成不同的小組來併購可以節省經費。 給定每份土地的尺寸,請你幫助他計算購買所有土地所需的最小費用。
\(\color{#0066ff}{輸入格式}\)
Line 1: A single integer: N
Lines 2..N+1: Line i+1 describes plot i with two space-separated integers: \(width_i\)
\(\color{#0066ff}{輸出格式}\)
Line 1: The minimum amount necessary to buy all the plots.
\(\color{#0066ff}{輸入樣例}\)
4
100 1
15 15
20 5
1 100
\(\color{#0066ff}{輸出樣例}\)
500
\(\color{#0066ff}{資料範圍與提示}\)
none
\(\color{#0066ff}{題解}\)
首先可以發現,對於兩個矩形, 如果一個可以完全包含另一個,完全可以讓這兩個一組,這樣那個小的矩形就沒貢獻了,可以刪掉
按長為第一關鍵字,寬為第二關鍵字從小到大排序
倒著掃一遍,維護max寬,如果當前矩形的寬小於max寬,又因為排序一定小於長
所以當前矩形就沒用了,否則放入一個新的數組裡
翻轉一下可以發現
這個陣列的矩形,長遞增,寬遞減
最優的分組一定是幾個連續的區間
可以發現一個區間的價值是l的長和r的寬
如果不連續,那麼價值要比這個大
所以就可以DP了
\(f[i] = f[j-1]+i_a*j_b\)
把\(i_a\)作為斜率移到左邊,因為是負的,而且還要維護最小值,斜率應為正的
可以直接取正的維護最大值然最後取反就行了
#include<bits/stdc++.h> #define LL long long LL in() { char ch; int x = 0, f = 1; while(!isdigit(ch = getchar()))(ch == '-') && (f = -f); for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48)); return x * f; } const int maxn = 50505; using std::pair; using std::make_pair; pair<LL, LL> mp[maxn], a[maxn]; int cnt; LL f[maxn]; LL X(int x) { return a[x + 1].second; } LL Y(int x) { return f[x]; } double K(int x, int y) { return (double)(Y(x) - Y(y)) / (double)(X(x) - X(y)); } int main() { int n = in(); for(int i = 1; i <= n; i++) mp[i].first = in(), mp[i].second = in(); std::sort(mp + 1, mp + n + 1); LL max = 0; for(int i = n; i >= 1; i--) { if(mp[i].second > max) a[++cnt] = mp[i]; max = std::max(max, mp[i].second); } std::reverse(a + 1, a + cnt + 1); static int q[maxn], head, tail; for(int i = 1; i <= cnt; i++) { LL k = a[i].first; while(head < tail && k > K(q[head], q[head + 1])) head++; f[i] = f[q[head]] - a[i].first * a[q[head] + 1].second; while(head < tail && K(q[tail], q[tail - 1]) > K(i, q[tail - 1])) tail--; q[++tail] = i; } printf("%lld\n", -f[cnt]); return 0; }