hdu 3669(斜率优化DP)

Cross the Wall

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

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

Source

2010 Asia Regional Harbin

Recommend

lcy   |   We have carefully selected several similar problems for you:  3662 3661 3664 3665 3666

题目描述:

给你n个人,每个人的宽度和高度已知,最多可以修k个门,修门的花费为门的面积,求最少花费。

d[i][j]=min(d[k][j-1]+p[k+1].w*p[i].h);时间复杂度为5*10^12,可以采用斜率优化或者四边形优化。

采取斜率优化:

设k1>k2,k1优于k2,dp[k1][j-1]+p[k1+1].w*p[i].h<=dp[k2][j-1]+p[k2+1].w*p[i].h;

化简得:dp[k1][j-1]-dp[k2][j-1]<p[i].h*(p[k2+1].w-p[k1+1].w)

预处理d[i][0]=inf,d[0][i]=0;

还需注意i和j的枚举顺序,先枚举j,再枚举i,每次枚举j的时候都需要重新初始化i的解集

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define maxn 50100
#define LL long long
#define inf 0x3f3f3f3f3f3f3f3f3f3f
using namespace std;
LL dp[maxn][200];
int que[maxn];
int n,k;
int head,tail;
struct node
{
    LL w;
    LL h;
};
node p[maxn];
bool cmp(node a,node b)
{
      if(a.w==b.w)
        return a.h<b.h;
      else
        return a.w>b.w;
}
LL getdp(int i,int j,int k)
{
    //return dp[j]+m+(sum[i]-sum[j])*(sum[i]-sum[j]);
    return dp[k][j-1]+p[k+1].w*p[i].h;
}

LL getup(int j,int k1,int k2) //yj-yk部分  k1>k2
{
    //return dp[j]+sum[j]*sum[j]-(dp[k]+sum[k]*sum[k]);
    return dp[k1][j-1]-dp[k2][j-1];
}
LL getdown(int j,int  k1,int k2)
{
    //return 2*(sum[j]-sum[k]);
    return p[k2+1].w-p[k1+1].w;
}

void solve()
{
    head=0;
    tail=0;
    que[tail++]=0;
    dp[0][0]=0;
    for(int i=1;i<=n;i++)
    {
         dp[i][0]=inf;
         dp[0][i]=0;
    }
    for(int j=1;j<=k;j++)
    {
         head=tail=0;
         que[tail++]=0;
         for(int i=1;i<=n;i++)
        {
     //从头开始找当前状态的最优决策,g[que[head+1],que[head]] < sum[i],说明que[head+1]比que[head]更优,删除que[head]
       while(head+1 < tail && getup(j,que[head+1],que[head]) <= getdown(j,que[head+1],que[head]) * p[i].h )
            head++;   //注意写成相乘,不然要考虑除数是否为负数
        dp[i][j]=getdp(i,j,que[head]);

    //从尾往前,加入当前状态,如果g[i,que[tail]] < g[que[tail],que[tail-1]] ,可以排除que[tail]
         while(head+1<tail && getup(j,i,que[tail-1])*getdown(j,que[tail-1],que[tail-2])<=getup(j,que[tail-1],que[tail-2])*getdown(j,i,que[tail-1]))
                    tail--;
          que[tail++]=i;
        }
    }

       /*for(int j=1;j<=k;j++)
        {
             for(int i=1;i<=n;i++)
            printf("%lld ",dp[i][j]);
            printf("\n");
        }*/
        printf("%lld\n",dp[n][k]);

}
int main()
{
    while(~scanf("%d%d",&n,&k))
    {
        //init();
        for(int i=1;i<=n;i++)
            scanf("%lld%lld",&p[i].w,&p[i].h);
            sort(p+1,p+n+1,cmp);

         int j=1;
         for(int i=2;i<=n;i++)
         {
             if(p[i].h<p[j].h)
             continue;
             else
             {
                 p[++j]=p[i];
             }
         }
         n=j;
          //for(int i=1;i<=n;i++)
        //   printf("%lld %lld\n",p[i].w,p[i].h);
            solve();
    }
    return 0;
}

四边形优化:

目前位置接触到两种形式的方程可以采用四边形优化:

1

a d[i][j]=min(d[i-1][k]+p[k+1].w*p[i].h)

写成这种形式,而不是上面那种,是因为四边形优化的s[i][j]的递推顺序好写

b s[i-1][j]<s[i][j]<s[i][j+1];

观察a和b式,i从小到大,j从大到小.然后初始化第一行和第n+1列,进行递推。

2 (类似于石子合并)

d[i][j]=d[i][k]+d[k+1][j]+w[i][j]

s[i][j-1]<s[i][j]<s[i+1][j]

这时候的递推顺序,是通过先枚举长度,再枚举起点,然后就可以现在要算的状态之前都算过了。

(超时代码,目前只能写到这了)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define maxn 50100
#define LL long long
#define inf 0x3f3f3f3f3f3f3f3f3f3f
using namespace std;
LL d[maxn][200];
int w[maxn][200];
int n,k;
struct node
{
    LL w;
    LL h;
};
node p[maxn];
bool cmp(node a,node b)
{
      if(a.w==b.w)
        return a.h<b.h;
      else
        return a.w>b.w;
}
void solve()
{
    for(int i=1;i<=n;i++)
    {
        d[1][i]=p[1].w*p[i].h;
        w[1][i]=0;

    }

       for(int i=2;i<=k;i++)
       {
             w[i][n+1]=n;
              for(int j=n;j>=i;j--)
            {
               d[i][j]=inf;
               for(int s=w[i-1][j];s<=w[i][j+1];s++)
                {
                    if(d[i][j]>(d[i-1][s]+p[s+1].w*p[j].h) )
                     {
                         d[i][j]=(d[i-1][s]+p[s+1].w*p[j].h);
                         w[i][j]=s;
                     }
                }
            }
       }
         /*for(int i=0;i<=k;i++)
        {
            for(int j=0;j<=n+1;j++)
            printf("%d ",w[i][j]);
            printf("\n");
        }
         printf("\n");
        for(int i=0;i<=k;i++)
        {
            for(int j=0;j<=n;j++)
            printf("%lld ",d[i][j]);
            printf("\n");
        }*/
       printf("%lld\n",d[k][n]);

}
int main()
{
    while(~scanf("%d%d",&n,&k))
    {
        //init();
        memset(d,0,sizeof(d));
        for(int i=1;i<=n;i++)
            scanf("%lld%lld",&p[i].w,&p[i].h);
            sort(p+1,p+n+1,cmp);

            int j=1;
         for(int i=2;i<=n;i++)
         {
             if(p[i].h<p[j].h)
                continue;
             else
             {
                 p[++j]=p[i];
             }
         }
         n=j;

            solve();
    }
    return 0;
}
时间: 2024-10-11 08:22:10

hdu 3669(斜率优化DP)的相关文章

Fxx and game hdu 5945 斜率优化dp

dfs你怕是要爆炸 考虑dp; 很容易想到 dp[ i ] 表示到 i 时的最少转移步数: 那么: dp[ i ]= min( dp[ i ],dp[ i-j ]+1 ); 其中 i-t<=j<=i; 当 i%k==0时 ,dp[ i ]=min( dp[ i ],dp[ i/k ]+1 ): 很明显这种要T到飞起: 我们要优化dp: 1e6的数据考虑O(n)级别的: 斜率优化: #include<iostream> #include<cstdio> #include&

HDU 4258 斜率优化dp

Covered Walkway Time Limit: 30000/10000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 1496    Accepted Submission(s): 602 Problem Description Your university wants to build a new walkway, and they want at least p

HDU 3507(斜率优化dp

题目:每次选取连续的若干数字的代价 要求选取虽有数字的最小代价. 思路:基础斜率dp题,题解见http://www.cnblogs.com/kuangbin/archive/2012/08/26/2657650.html /* * @author: Cwind */ #pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include &

HDU 3507斜率优化dp

Print Article Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)Total Submission(s): 12185    Accepted Submission(s): 3733 Problem Description Zero has an old printer that doesn't work well sometimes. As it is antiqu

hdu 2993 MAX Average Problem (斜率优化dp入门)

MAX Average Problem Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 5855    Accepted Submission(s): 1456 Problem Description Consider a simple sequence which only contains positive integers as

Print Article hdu 3507 一道斜率优化DP 表示是基础题,但对我来说很难

Print Article Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)Total Submission(s): 4990    Accepted Submission(s): 1509 Problem Description Zero has an old printer that doesn't work well sometimes. As it is antique

HDU 2829 Lawrence (斜率优化DP或四边形不等式优化DP)

题意:给定 n 个数,要你将其分成m + 1组,要求每组数必须是连续的而且要求得到的价值最小.一组数的价值定义为该组内任意两个数乘积之和,如果某组中仅有一个数,那么该组数的价值为0. 析:DP状态方程很容易想出来,dp[i][j] 表示前 j 个数分成 i 组.但是复杂度是三次方的,肯定会超时,就要对其进行优化. 有两种方式,一种是斜率对其进行优化,是一个很简单的斜率优化 dp[i][j] = min{dp[i-1][k] - w[k] + sum[k]*sum[k] - sum[k]*sum[

HDU 3507 Print Article(斜率优化DP)

题目链接 题意 : 一篇文章有n个单词,如果每行打印k个单词,那这行的花费是,问你怎么安排能够得到最小花费,输出最小花费. 思路 : 一开始想的简单了以为是背包,后来才知道是斜率优化DP,然后看了网上的资料,看得还挺懂的,不过我觉得如果以后真遇到斜率DP,要推起来肯定不简单..... 网上资料1 网上资料2 1 #include <iostream> 2 #include <stdio.h> 3 4 using namespace std; 5 6 int q[500005],dp

斜率优化DP总结

前言: 也是好久没有写题解了,最近主要学习了单调栈单调队列以及斜率优化DP这几个知识点,对于较难的斜率优化DP,做个小小的总结吧. 正(che)文(dan): T1 hdu 3507 在一个风和日丽的早上,你打开了网页,点进了hdu,偶然间看到了这道题,不屑的以为这仅仅是一个很水的DP,2分钟给出DP方程式,很快的写完后发现n的范围居然是500000,这让已经推出来的 O(n2)复杂度的递推式情何以堪,所以就产生了一种高逼格的优化方式:斜率优化. 这道题的方程式是什么呢? dp[i]=min(d