ZOJ2860----DP+四边形不等式的优化

题目地址:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1860

这个题目的意思和UVA10003很像,都是从一个木棍上进行m次切割。

每次切割都会有一个value为当前切割的木棍的长度

这个题目给的长度有10000000,非常的大,直接开二维数组必爆。

所以对每一个切割的点都保存在一个数组中,相当于离散化了

并且把a[0]=0, a[m+1]=n

然后再进行DP

状态转移方程为:

dp[i][j] = min(dp[i][k]+dp[k][j]+a[j]-a[i])  i<k<j

这是一个n^3的算法,然后就果断TLE了。

怎么优化?黑书的P152给了答案

当函数w(i,j)满足 w(a,c)+w(b,d) <= w(b,c)+w(a,d) 且a<=b< c <=d 时,我们称w(i,j)满足四边形不等式。。
当函数w(i, j)满足w(i', j) <= w(i, j'); i <= i' < j <= j' 时,称w关于关于区间包含关系单 调。
s(i, j)=k是指m(i, j)这个状态的最优决策
以上定理的证明自己去查些资料
今天看得lrj的书中介绍的 四边形优化  做个笔记,加强理解 
最有代价用d[i,j]表示 
d[i,j]=min{d[i,k-1]+d[k+1,j]}+w[i,j] 
其中w[i,j]=sum[i,j] 
四边形不等式   
     w[a,c]+w[b,d]<=w[b,c]+w[a,d](a<b<c<d) 就称其满足凸四边形不等式 
决策单调性 
     w[i,j]<=w[i',j']   ([i,j]属于[i',j']) 既 i'<=i<j<=j'
于是有以下三个定理 

定理一: 如果w同时满足四边形不等式 和 决策单调性 ,则d也满足四边形不等式
定理二:当定理一的条件满足时,让d[i,j]取最小值的k为K[i,j],则K[i,j-1]<=K[i,j]<=K[i+1,j] 
定理三:w为凸当且仅当w[i,j]+w[i+1,j+1]<=w[i+1,j]+w[i,j+1] 

由定理三知 判断w是否为凸即判断 w[i,j+1]-w[i,j]的值随着i的增加是否递减 
于是求K值的时候K[i,j]只和K[i+1,j] 和 K[i,j-1]有关,所以 可以以i-j递增为顺序递推各个状态值最终求得结果  将O(n^3)转为O(n^2) 

通过这样的一个DP优化,就大大的提高了DP的速度

并且由于这个数据很强,最大值非常大,所以要注意,我WA了三次

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn  = 1000+10;
const long long inf = 10000000*10;
#define LL long long
LL dp[maxn][maxn];
LL s[maxn][maxn];
int a[maxn];
int n,m;

void dpp(int low,int high)
{
    for(int i=low;i<=high;i++)
    {
        dp[i][i] = 0;
        dp[i][i+1] = 0;
        s[i][i+1] = i;
        s[i][i] = i;
    }

    for(int len=3;len<=high+1;len++)
    {
        for(int i=low;i<=high-len+1;i++)
        {
            int j=i+len-1;
            dp[i][j] = inf;
            for(int k=s[i][j-1];k<=s[i+1][j];k++)
            {
                if(dp[i][k]+dp[k][j]+a[j]-a[i]<dp[i][j])
                {
                    dp[i][j] = dp[i][k]+dp[k][j]+a[j]-a[i];
                    s[i][j] = k;
                }
            }
        }
    }
}


int main()
{

    while(~scanf("%d%d",&n,&m))
    {
        for(int i=1;i<=m;i++)
            scanf("%d",&a[i]);
        a[0]=0;
        a[m+1]=n;
        dpp(0,m+1);
        printf("%lld\n",dp[0][m+1]);
    }
    return 0;
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值