斜率优化DP

来源:互联网 发布:国产电视机 知乎 编辑:程序博客网 时间:2024/06/10 10:06
  • HDU - 3507 Print Article
  • HDU - 2829 Lawrence
  • HDU - 1300 Pearls
  • HDU 3480 Division
  • HYSBZ 1010 玩具装箱toy
  • HYSBZ - 1096 仓库建设

HDU - 3507
入门题。

#include <cstdio>#include <iostream>#include <cstring>#include <algorithm>using namespace std;#define ll long longconst int maxn = 5e5 + 10;ll d[maxn],sum[maxn],dp[maxn];int n,m;ll getx(int j,int k){   return dp[j] + sum[j]*sum[j] - (dp[k] + sum[k]*sum[k]); }ll gety(int j,int k){   return 2*sum[j]-2*sum[k];   }ll getval(int i,int j){   return dp[j] + (sum[i]-sum[j])*(sum[i]-sum[j]) + m;   }int que[maxn];int main(){    while(~scanf("%d%d",&n,&m))    {        for(int i = 1; i <= n; i++)        {            scanf("%I64d",&d[i]);            sum[i] = d[i];            if(i) sum[i] += sum[i-1];            dp[i] = 0;        }        int head = 0,tail = 1;        que[0] = 0;        for(int i = 1; i <= n; i++)        {            while(tail > head+1 && getx(que[head+1],que[head]) <= sum[i]*gety(que[head+1],que[head])) ++head;            dp[i] = getval(i,que[head]);            while(tail > head+1 && getx(i,que[tail-1])*gety(que[tail-1],que[tail-2]) <= getx(que[tail-1],que[tail-2])*gety(i,que[tail-1]))  tail--;            que[tail++] = i;        }        printf("%I64d\n", dp[n]);    }    return 0;}

HDU - 2829
题目大意:给你m个炸弹炸铁路,最后问你铁路最小价值和是多少。
思路:可以推出一个很神奇的式子:
dp[i][j]=min(dp[k][j1]+val(k+1,i));
val(k+1,i)=im=k+1d[m](sum[m1]sum[k])
所以有:
val(k+1,i)=im=k+1d[m]sum[m1]im=k+1d[m]sum[k]
然后设
fn[i]=im=1d[m]sum[m1]
发现这个fn[i,k] = fn[i] - fn[k-1],可以O(1)查询,满足区间减法!所以这个时候我们的前提条件就准备好啦。

代码我是设的 sum[i]=i1m=1d[m] 所以fn的表示有一点不同,sum[i]的表示也不同。

#include <cstdio>#include <iostream>#include <cstring>#include <algorithm>using namespace std;const int maxn = 1e3+10;#define ll long longint d[maxn];ll dp[maxn][maxn],fn[maxn],sum[maxn];ll getval(int i,int k,int j){    ll temp = fn[i] - fn[k] - sum[k+1]*(sum[i+1]-sum[k+1]);    return temp+dp[k][j-1];}ll getx(int j,int k,int lev){    ll a = dp[j][lev-1] - fn[j] + sum[j+1]*sum[j+1];    ll b = dp[k][lev-1] - fn[k] + sum[k+1]*sum[k+1];    return a-b;}int que[maxn];ll gety(int j,int k){   return sum[j+1] - sum[k+1]; }int n,m;int main(){    //freopen("D:\\in.txt","r",stdin);    while(scanf("%d%d",&n,&m) && n+m)    {        for(int i = 1; i <= n; i++) scanf("%d",&d[i]);        for(int i = 1; i <= n; i++)        {            sum[i] = d[i-1] + sum[i-1];            fn[i] = sum[i]*d[i] + fn[i-1];        }        sum[n+1] = d[n] + sum[n];        for(int i = 1; i <= n; i++)            dp[i][1] = getval(i,0,1);        for(int j = 2; j <= m+1; j++)        {            int h = 0, t = 1;            que[h] = j-1;que[t++] = j;            for(int i = 0; i <= j; i++) dp[i][j] = 0;            for(int i = j+1; i <= n;i++)            {                int T = sum[i+1];                while(h+1 < t && getx(que[h+1],que[h],j) <= T*gety(que[h+1],que[h])) h++;                dp[i][j] = getval(i,que[h],j);                while(h+1<t && getx(i,que[t-1],j)*gety(que[t-1],que[t-2]) <= getx(que[t-1],que[t-2],j)*gety(i,que[t-1])) t--;                que[t++] = i;            }        }        printf("%I64d\n",dp[n][m+1]);    }    return 0;}

HDU - 1300
题意:这题其实最难的是题意,其实我也就看了个大概
有n种珍珠,每种珍珠有一个需求量,有单价,排在前面的可以用后面的价格买,排在后面的不能用前面的价格买。买的时候是这个式子:cost=(num+10)unit
思路:先列dp方程
dp[i]=dp[k]+val(k+1,i)
val(k+1,i)=c[i](sum[i]sum[k]+10)
然后的事情就很简单了。

#include <cstdio>#include <iostream>#include <cstring>#include <algorithm>using namespace std;const int maxn = 105;int d[maxn],c[maxn];int sum[maxn];int dp[maxn],q[maxn];int getavl(int i,int k){    return dp[k] + c[i]*(sum[i]-sum[k] + 10);}int getx(int j,int k){  return dp[j]-dp[k];     }int gety(int j,int k){  return sum[j]-sum[k];   }int n;int main(){    int t;    scanf("%d",&t);    while(t--)    {        scanf("%d",&n);        for(int i = 1; i <= n; i++)        {            scanf("%d%d",&d[i],&c[i]);            sum[i] = d[i] + sum[i-1];        }        int h = 0,t = 0;        q[t++] = 0;        for(int i = 1; i <= n; i++)        {            int T = c[i];            while(h+1<t && getx(q[h+1],q[h]) <= gety(q[h+1],q[h])*T) ++h;            dp[i] = getavl(i,q[h]);            while(h+1<t && getx(i,q[t-1])*gety(q[t-1],q[t-2]) <= getx(q[t-1],q[t-2])*gety(i,q[t-1])) --t;            q[t++] = i;        }        cout << dp[n] << endl;    }    return 0;}

HDU - 3480
题意: 把一个序列分成m段,使每段的最大值减最小值的平方和最小。
思路:我们需要先排个序,然后再处理
可以推出dp方程:dp[i][j]=min(dp[k][j1]+(d[i]d[k+1])(d[i]d[k+1]));
由于数组很大,所以需要一下滚动数组。

#include <cstdio>#include <iostream>#include <cstring>#include <algorithm>using namespace std;const int maxn = 1e4 + 10;int d[maxn];int dp[maxn][2];int getval(int i,int k,int j){    int last = j==0?1:0;    return dp[k][last] + (d[i]-d[k+1])*(d[i]-d[k+1]);}int getx(int j,int k,int th){    int last = th==0?1:0;    return dp[j][last]+d[j+1]*d[j+1] - (dp[k][last] + d[k+1]*d[k+1]);}int gety(int j,int k){return d[j+1]-d[k+1];}int q[maxn];int n,m;int main(){    int t,kase=1;    scanf("%d",&t);    while(t--)    {        scanf("%d%d",&n,&m);        for(int i = 1; i <= n; i++) scanf("%d",&d[i]);        sort(d + 1, d + n + 1);        int th = 0,last = 0;        for(int i = 1; i <= n; i++) dp[i][0] = getval(i,0,th);        for(int j = 2;  j <= m; j++)        {            th = last?0:1;            for(int i = 1; i <= j; i++) dp[i][th] = 0;            int h = 0,t = 0;            q[t++] = j-1; q[t++] = j;            for(int i = j+1; i <= n; i++)            {                int T = d[i]*2;                while(h+1<t && getx(q[h+1],q[h],th) <= T*gety(q[h+1],q[h])) h++;                dp[i][th] = getval(i,q[h],th);                while(h+1<t && getx(i,q[t-1],th)*gety(q[t-1],q[t-2]) <= getx(q[t-1],q[t-2],th)*gety(i,q[t-1])) t--;                q[t++] = i;            }            last = th;        }        printf("Case %d: %d\n", kase++, dp[n][th]);    }    return 0;}

BZOJ - 1010
题意:中文题
思路:dp[i]=dp[j]+i(j+1)+sum[i]sum[j];

#include <cstdio>#include <iostream>#include <cstring>#include <algorithm>using namespace std;#define ll long longconst int maxn = 5e4 + 10;ll d[maxn],sum[maxn],dp[maxn];int que[maxn];int n,m;ll getval(int i,int j){    ll temp = i - (j+1) + sum[i] - sum[j];    return (ll)dp[j] + (temp-m)*(temp-m);}ll getx(int j,int k){    ll a = dp[j] + (j+1+sum[j]+m)*(j+1+sum[j]+m);    ll b = dp[k] + (k+1+sum[k]+m)*(k+1+sum[k]+m);    return a-b;}ll gety(int j,int k){    return j+sum[j]-k-sum[k];}int main(){    scanf("%d%d",&n,&m);    for(int i = 1; i <= n; i++)    {        scanf("%lld",&d[i]);        sum[i] = d[i];        sum[i] += sum[i-1];        dp[i] = 0;    }    int head = 0, tail = 1;    que[0] = 0;    for(int i = 1; i <= n; i++)    {        ll T = 2*(i+sum[i]);        while(tail > head+1 && getx(que[head+1],que[head]) <= T*gety(que[head+1],que[head]))  head++;        dp[i] = getval(i,que[head]);        while(tail > head+1 && getx(i,que[tail-1])*gety(que[tail-1],que[tail-2]) <= getx(que[tail-1],que[tail-2])*gety(i,que[tail-1])) tail--;        que[tail++] = i;    }    printf("%lld\n",dp[n]);    return 0;}

BZOJ - 1096
中文题+2
思路:
dp[i]=dp[j]+dp[k]fn[i]+fn[k]+d[i][0](sum[i]sum[k])+d[i][2]

#include <cstdio>#include <iostream>#include <cstring>#include <algorithm>using namespace std;#define ll long longconst int maxn = 1e6 + 10;ll d[maxn][3];ll sum[maxn], fn[maxn];int que[maxn];ll dp[maxn];ll setval(int i,int k){   ll temp = dp[k] - fn[i] + fn[k] + d[i][0]*(sum[i]-sum[k]) + d[i][2];    return temp;}ll fx(int j,int k){    ll a = dp[j] + fn[j] ;    ll b = dp[k] + fn[k] ;    return a-b;}ll fy(int j,int k){    return sum[j] - sum[k];}int main(){    //freopen("D:\\in.txt","r",stdin);    int n;    scanf("%d",&n);    for(int i = 1; i <= n; i++)    {        scanf("%lld%lld%lld",&d[i][0],&d[i][1],&d[i][2]);        ll temp = d[i][0]*d[i][1];        fn[i] = temp;       sum[i] = d[i][1];        fn[i] += fn[i-1];   sum[i] += sum[i-1];    }    int h = 0,t = 1;    que[0] = 0;    for(int i = 1; i <= n; i++)    {        ll T = d[i][0];        while(h+1<t && fx(que[h+1],que[h]) <= fy(que[h+1],que[h])*T) h++;        dp[i] = setval(i,que[h]);        while(h+1<t && fx(i,que[t-1])*fy(que[t-1],que[t-2]) <= fx(que[t-1],que[t-2])*fy(i,que[t-1])) --t;        que[t++] = i;    }    printf("%lld\n",dp[n]);    return 0;}
原创粉丝点击