BZOJ 1597 [Usaco2008 Mar]土地購買:斜率優化dp

Leohh發表於2018-02-03

題目連結: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 }

 

相關文章