HDU_3669 斜率优化DP

虽然是一道水题,TLE+WA了一天,WA的原因是因为自己粗心了,DP的范围明明超过了int,强行用int来写。(不过很快就改过来了,然后T了一天)至于T的原因,只能说自己太蠢,HDU给了我一种错觉,总觉得C++跑得要比G++快,然后加了10发C++全T了,最后抱着试试看的心情交了一发G++,AC了。。。只跑了452ms。。。然后把第一次敲的代码放上去,也妥妥地AC了。。。唉。。。

首先,这道题的转移方程比较好写:dp[i][m] = min{dp[j][m-1]+w[i]*h[j+1]}。在这之前,我们需要把每一个矩形进行排序,按照w排(h排也行),然后w相同的按照h排,顺序都是从大到小,目的就是去掉w[j]<=w[i] && h[j]<=h[i]的矩形,因为这样的矩形没有任何意义。
然后对于: i > j > K:
G(j,k) = (dp[j][m-1] - dp[k][m-1])/(h[k+1]-h[j+1]) < w[i] :j点比k点优,k点可以删除,因为w[i]单调的,所以后面的点,j点总是比k点优。
G(j,k) >= G(i,j):则j点可以删除。
这道题由一维变成了二维,其实,只需要用队列维护dp[i][pre]的凸包就好了。

因为对内存要求不是很高,所以直接搞就好了,如果题目对内存有要求,那么最好换成滚动数组来写。
下面附上AC代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <map>
#include <queue>
#include <stack>
#define ll  long long
#define FOR(i,x,y)  for(int i = x;i < y;i ++)
#define N   55555
#define K   111

using namespace std;

typedef pair<ll,ll> P;
ll dp[N][K];
int n,k,cnt;
ll w[N],h[N],w_tem[N],h_tem[N],q[N],tail,head;
P ret[N];

ll G_UP(int i,int j,int k){
    return (dp[i][k]-dp[j][k]);
}

ll G_DOWN(int i,int j){
    return (h[j+1]-h[i+1]);
}

ll G_DP(int i,int j,int k){
    return dp[j][k]+w[i]*h[j+1];
}

bool cmp(P a,P b){
    if(a.first < b.first)   return true;
    if(a.first == b.first && a.second < b.second)   return true;
    return false;
}

void init(){
    cnt = 0;
    w_tem[cnt] = ret[n-1].first;
    h_tem[cnt++] = ret[n-1].second;
    for(int i = n-2;i >= 0;i--){
        if(ret[i].second <= h_tem[cnt-1])   continue;
        w_tem[cnt] = ret[i].first;
        h_tem[cnt++] = ret[i].second;
    }
    FOR(i,0,cnt){
        w[i] = w_tem[cnt-1-i];
        h[i] = h_tem[cnt-1-i];
    }
    FOR(i,0,cnt){
        dp[i][1] = w[i] * h[0];
    }
}

void solve(){
    if(k > cnt)    k = cnt;
    FOR(m,2,k+1){
        head = tail = 0;
        q[tail++] = m-2;
        for(int i = m-1;i < cnt;i ++){
            while(head+1 < tail && (G_UP(q[head+1],q[head],m-1)) <= (G_DOWN(q[head+1],q[head])*w[i]))   head++;
            dp[i][m] = G_DP(i,q[head],m-1);
            while(head+1 < tail && (G_UP(q[tail-1],q[tail-2],m-1)*G_DOWN(i,q[tail-1]) >= G_DOWN(q[tail-1],q[tail-2])*G_UP(i,q[tail-1],m-1)))
            tail--;
            q[tail++] = i;
        }
    }
}

int main()
{
    //freopen("test.in","r",stdin);
    while(~scanf("%d%d",&n,&k)){
        ll x,y;
        FOR(i,0,n){
            scanf("%I64d%I64d",&x,&y);
            ret[i] = make_pair(x,y);
        }
        sort(ret,ret+n,cmp);
        init();
        solve();
        ll ans = dp[cnt-1][1];
        FOR(i,2,k+1){
            ans = min(ans,dp[cnt-1][i]);
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

附上滚动数组版:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <map>
#include <queue>
#include <stack>
#define N   50010
#define K   105

using namespace std;

typedef long long ll;

struct Node{
    ll x,y;
}ret[N];
ll dp[N][2];
int n,k,cnt;
ll w[N],h[N],w_tem[N],h_tem[N],q[N],tail,head;
ll ans;

ll G_UP(int i,int j,int k){
    return (dp[i][k]-dp[j][k]);
}

ll G_DOWN(int i,int j){
    return (h[j+1]-h[i+1]);
}

ll G_DP(int i,int j,int k){
    return dp[j][k]+w[i]*h[j+1];
}

bool cmp(Node a,Node b){
    if(a.x < b.x)   return true;
    if(a.x == b.x && a.y < b.y)   return true;
    return false;
}

void init(){
    cnt = 0;
    w_tem[cnt] = ret[n-1].x;
    h_tem[cnt++] = ret[n-1].y;
    for(int i = n-2;i >= 0;i--){
        if(ret[i].y <= h_tem[cnt-1])   continue;
        w_tem[cnt] = ret[i].x;
        h_tem[cnt++] = ret[i].y;
    }
    for(int i = 0;i < cnt;i ++){
        w[i] = w_tem[cnt-1-i];
        h[i] = h_tem[cnt-1-i];
    }
    for(int i = 0;i < cnt;i ++){
        dp[i][0] = w[i] * h[0];
    }
    ans = dp[cnt-1][0];
}

void solve(){
    if(k >= cnt)    k = cnt;
    int pre = 0,cur = 1;
    for(int m = 2;m <= k;m ++){
        head = tail = 0;
        q[tail++] = m-2;
        for(int i = m-1;i < cnt;i ++){
            while(head+1 < tail && (G_UP(q[head+1],q[head],pre)) < (G_DOWN(q[head+1],q[head])*w[i]))   head++;
            dp[i][cur] = G_DP(i,q[head],pre);
            while(head+1 < tail && (G_UP(q[tail-1],q[tail-2],pre)*G_DOWN(i,q[tail-1]) >= G_DOWN(q[tail-1],q[tail-2])*G_UP(i,q[tail-1],pre)))
            tail--;
            q[tail++] = i;
        }
        ans = min(ans,dp[cnt-1][cur]);
        cur = 1-cur;
        pre = 1-pre;
    }
}

int main()
{
    //freopen("test.in","r",stdin);
    while(~scanf("%d%d",&n,&k)){
        for(int i = 0;i < n;i ++){
            scanf("%I64d%I64d",&ret[i].x,&ret[i].y);
        }
        sort(ret,ret+n,cmp);
        init();
        solve();
        printf("%I64d\n",ans);
    }
    return 0;
}



posted @ 2015-05-21 15:06  hqwhqwhq  阅读(140)  评论(0编辑  收藏  举报