zoukankan      html  css  js  c++  java
  • 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
    ",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
    ",ans);
        }
        return 0;
    }



  • 相关阅读:
    认识Cookie和状态管理
    python之requests库使用问题汇总
    如何掌握所有的程序语言--王垠
    非 GUI 模式运行 JMeter 压力测试
    Python 如何将字符串转为字典
    python之operator操作符函数
    python3使用xlrd、xlwt、xlutils、openpyxl、xlsxwriter操作excel
    《敏捷软件测试:测试人员与敏捷团队的实践指南》
    Jmeter跨线程组传递变量
    Jmeter获取当前时间、历史时间、未来时间的方式
  • 原文地址:https://www.cnblogs.com/hqwhqwhq/p/4555873.html
Copyright © 2011-2022 走看看