hdu 3516 Tree Construction 四边形不等式优化

题目链接


题意:

二维坐标上,给出n个点,x严格单增,y严格单减。要求将这些点构成一棵树,其中边的方向只能是x轴正向和y轴正向。问树中所有边的长度之和最短为多少。


分析:

因为规定了变得方向,所以根节点坐标应该不在第一个点右边,也应该不在最后一个点上面。容易感觉到根节点一定就在(a[1].x,a[n].y)处。
dp[le][ri]=min{dp[le][k]+dp[k+1][ri]+a[k+1].x-a[le].x+a[k].y-a[ri].y}

这个题目的四边形不等式不会证(从来就没会过)。

下面证明在满足四边形不等式的前提下,决策满足单调性:

令m[le,ri]=dp[le][ri],该区间的决策k为s[le,ri]

有m[le,ri]=m[le,k]+m[k+1,ri]+a[k+1].x-a[le].x+a[k].y-a[ri].y

下面证明s[i,j-1]<=s[i,j]<=s[i+1,j]
先证明s[i,j]<=s[i+1,j]

设 d=s[i,j],设k<=d
则有 mk[i,j]>=md[i,j]
下面需要证明 mk[i+1,j]>=md[i+1,j]

引理: mk[i+1,j]md[i+1,j]>=mk[i,j]md[i,j]

由引理知
mk[i+1,j]md[i+1,j]>=mk[i,j]md[i,j]>=0

mk[i+1,j]>=md[i+1,j] 即[i+1,j]的决策点绝不会在[i,j]决策点左边。

证毕。

关于引理的证明:
mk[i+1,j]md[i+1,j]>=mk[i,j]md[i,j]>=0

=mk[i+1,j]md[i+1,j]

=m[i+1,k]+m[k+1,j]+a[k].ya[j].y+a[k+1].xa[i].x

(m[i+1,d]+m[d+1,j]+a[d].ya[j].y+a[d+1].xa[i]x)

=m[i+1,k]+m[k+1,j]+a[k].y+a[k+1].x

(m[i+1,d]+m[d+1,j]+a[d].y+a[d+1].x)

<script type="math/tex; mode=display" id="MathJax-Element-100"></script>
=m[i,k]+m[k+1,j]+a[k].y+a[k+1].x

(m[i,d]+m[d+1,j]+a[d].y+a[d+1].x)

所以待证式子等价于
m[i+1,k]m[i+1,d]>=m[i,k]m[i,d] (消去,化简)

m[i+1,k]+m[i,d]>=m[i,k]+m[i+1,d]

因为k∈[i+1,j] (这是因为考虑[i+1,j]内的决策点), 所以
i<i+1<=k<=d ,满足四边形不等式的条件,所以
m[i+1,k]+m[i,d]>=m[i,k]+m[i+1,d],引理证毕。


代码:

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

#define for1(a, n) for (int (a) = 1; (a) <= (n); (a)++)
#define ysk(x)  (1<<(x))
const int INF =0x3f3f3f3f;
const int maxn=  1000 ;
int n,s[maxn+10][maxn+10],dp[maxn+10][maxn+10];
struct Node
{
    int x,y;
}a[maxn+10];

int main()
{
   std::ios::sync_with_stdio(false);
   while(cin>>n)
   {
       for1(i,n)  cin>>a[i].x>>a[i].y;

       for1(i,n)
       {
           dp[i][i]=0;
           s[i][i]=i;
       }

       for(int add=1;add<n;add++)
       {
           for(int le=1;le+add<=n;le++)
           {
               int ri=le+add;
               int L=s[le][ri-1];
               int R=s[le+1][ri];
               dp[le][ri]=INF;
               for(int k=L;k<=R&&k<ri;k++)
               {
                   int ret=dp[le][k]+dp[k+1][ri]+a[k+1].x-a[le].x+a[k].y-a[ri].y;
                   if(ret<=dp[le][ri])
                   {
                      dp[le][ri]=ret;
                      s[le][ri]=k;
                   }
               }

           }
       }
       printf("%d\n",dp[1][n]);

   }
   return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值