題目連結:http://www.lydsy.com/JudgeOnline/problem.php?id=1597
題意:
有n塊矩形土地,長為a[i],寬為b[i]。
FJ想要將這n塊土地全部買下來。
土地可以分組購買。
若有某一些土地被分到了一組,則將這一組土地全部買下的花費為他們的max(a[i])*max(b[i])。
問你最小總花費是多少。
題解:
首先,如果一塊土地能夠被另一塊土地完全包含(即長寬都比另一個小),那麼被包含的那塊土地可以忽略不計。
然後貪心地考慮如何使花費最小:
如果要使花費盡可能小,則分在同一組中矩形的長寬差距應儘可能地小。
所以將長a[i]作為第一關鍵字,將寬b[i]作為第二關鍵字排序。
此時矩形的長a[i]一定是非降的,那麼對於兩個矩形i和j(i<j),如果有b[i]<=b[j],則j一定能完全包含i。
利用這一特性將所有能被包含的矩形去掉。
此時矩形的寬b[i]就變成了嚴格遞減的了。
顯然,此時分到一組中的矩形必須是連續的一段區間,才有可能最優。
然後就可以dp了。
表示狀態:
dp[i]表示已經購買了前i塊土地的最小花費。
找出答案:
假設去掉能被包含的矩形之後,矩形總數為tot。
ans = dp[tot]
如何轉移:
dp[i] = min dp[j] + a[i]*b[j+1]
邊界條件:
dp[0] = 0
斜率優化:
設j < k,且k的決策更優。
則:dp[j] + a[i]*b[j+1] > dp[k] + a[i]*b[k+1]
整理得:(dp[k]-dp[j])/(b[j+1]-b[k+1]) < a[i]
所以slope(i,j) = (dp[i]-dp[j])/(b[j+1]-b[i+1])
由於a[i]非降,所以用單調佇列維護下凸殼即可。
AC Code:
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #include <algorithm> 5 #define MAX_N 50005 6 7 using namespace std; 8 9 struct Rect 10 { 11 long long a,b; 12 Rect(long long _a,long long _b) 13 { 14 a=_a; b=_b; 15 } 16 Rect(){} 17 friend bool operator < (const Rect &x,const Rect &y) 18 { 19 return x.a!=y.a ? x.a<y.a : x.b<y.b; 20 } 21 }; 22 23 int n; 24 int q[MAX_N]; 25 long long dp[MAX_N]; 26 Rect x[MAX_N]; 27 28 void read() 29 { 30 cin>>n; 31 for(int i=1;i<=n;i++) 32 { 33 cin>>x[i].a>>x[i].b; 34 } 35 } 36 37 inline double slope(int i,int j) 38 { 39 return (double)(dp[i]-dp[j])/(x[j+1].b-x[i+1].b); 40 } 41 42 void work() 43 { 44 sort(x+1,x+1+n); 45 int tot=0; 46 for(int i=1;i<=n;i++) 47 { 48 while(tot && x[i].b>=x[tot].b) tot--; 49 x[++tot]=x[i]; 50 } 51 x[tot+1]=Rect(0,0); 52 int l=1,r=1; 53 q[1]=0,dp[0]=0; 54 for(int i=1;i<=tot;i++) 55 { 56 while(l<r && slope(q[l],q[l+1])<=x[i].a) l++; 57 dp[i]=dp[q[l]]+x[i].a*x[q[l]+1].b; 58 while(l<r && slope(q[r],i)<slope(q[r-1],q[r])) r--; 59 q[++r]=i; 60 } 61 cout<<dp[tot]<<endl; 62 } 63 64 int main() 65 { 66 read(); 67 work(); 68 }