首页 > 代码库 > hdu3669之二维斜率DP

hdu3669之二维斜率DP

Cross the Wall

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 327680/327680 K (Java/Others)
Total Submission(s): 4176    Accepted Submission(s): 748


Problem Description
“Across the Great Wall, we can reach every corner in the world!” Now the citizens of Rectland want to cross the Great Wall. 
The Great Wall is a huge wall with infinite width and height, so the only way to cross is to dig holes in it. All people in Rectland can be considered as rectangles with varying width and height, and they can only dig rectangle holes in the wall. A person can pass through a hole, if and only if the person’s width and height is no more than the hole’s width and height both. To dig a hole with width W and height H, the people should pay W * H dollars. Please note that it is only permitted to dig at most K holes for security consideration, and different holes cannot overlap each other in the Great Wall. Remember when they pass through the wall, they must have their feet landed on the ground.
Given all the persons’ width and height, you are requested to find out the minimum cost for digging holes to make all the persons pass through the wall.
 

Input
There are several test cases. The first line of each case contains two numbers, N (1 <= N <= 50000) and K (1 <= K <= 100), indicating the number of people and the maximum holes allowed to dig. Then N lines followed, each contains two integers wi and hi (1 <= wi, hi <= 1000000), indicating the width and height of each person.
 

Output
Output one line for each test case, indicates the minimum cost.

 

Sample Input
2 1 1 100 100 1 2 2 1 100 100 1
 

Sample Output
10000 200
/*分析: 
对所有人进行h从大到小排序,然后扫描数组的时候只扫描w递增的
如果当前w比前面的w小则该点根本不需要,因为可以随便加入到前面已经开辟的洞中 
假定:
dp[i][j]表示前i个人打j个洞耗费的最少面积
则dp[i][j]=Min(dp[k][j-1]+w[i]*h[k+1])
=>dp[i][j]=dp[k][j-1]+w[i]*h[k+1]
这里有w[i]*h[k+1]所以不能直接用单调队列维护最小值
假定k2<k前i个数在k点分割取得最优值,h[k2+1]>=h[k+1]
则满足:
dp[k][j-1]+w[i]*h[k+1]<=dp[k2][j-1]+w[i]*h[k2+1]
=>(dp[k][j-1]-dp[k2][j-1])/(-h[k+1]-(-h[k2+1]))<=w[i]
假定:
y1=dp[k][j-1]
x1=-h[k+1];
y2=dp[k2][j-1]
x2=-h[k2+1];
所以变成(y1-y2)/(x1-x2)<=sum[i]
斜率DP,维护斜率递增(下凸斜率折线)即可 
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <queue>
#include <algorithm>
#include <map>
#include <iomanip>
#define INF 1ll<<60
typedef long long LL;
using namespace std;

const int MAX=50000+10;
int n,k,index,head,tail,Id;
LL dp[MAX][2],q[MAX];//dp采用滚动数组形式

struct Node{
    LL w,h;
    bool operator<(const Node &a)const{
        return h>a.h;
    }
}s[MAX];

LL GetY(int k,int k2){
    return dp[k][index^1]-dp[k2][index^1];
}

LL GetX(int k,int k2){
    return s[k2+1].h-s[k+1].h;
}

void DP(){
    index=0;
    memset(dp,0,sizeof dp);
    for(int i=1;i<=n;++i)dp[i][index]=INF;
    for(int j=1;j<=k;++j){
        index=index^1;
        head=tail=0;
        q[tail++]=0;
        Id=0;
        for(int i=1;i<=n;++i){
        	if(s[i].w <= s[Id].w)continue;
        	Id=i;
            while(head+1<tail && GetY(q[head+1],q[head])<=GetX(q[head+1],q[head])*s[i].w)++head;
            dp[i][index]=dp[q[head]][index^1]+s[i].w*s[q[head]+1].h;
            while(head+1<tail && GetY(i,q[tail-1])*GetX(q[tail-1],q[tail-2])<=GetY(q[tail-1],q[tail-2])*GetX(i,q[tail-1]))--tail;
            q[tail++]=i;
        }
    }
}

int main(){
    while(~scanf("%d%d",&n,&k)){
        for(int i=1;i<=n;++i)scanf("%I64d%I64d",&s[i].w,&s[i].h);
        sort(s+1,s+n+1);
        DP();
        printf("%I64d\n",dp[Id][index]);
    }
    return 0;
}
/*
3 1
2 6
4 4
2 2
*/