HDU_3669 斜率优化DP

来源:互联网 发布:怎么查4g网络覆盖 编辑:程序博客网 时间:2024/05/16 07:19

虽然是一道水题,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   111using 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   105using 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;}



0 0
原创粉丝点击