HDU - 3516 Tree Construction(区间dp)

传送门

Consider a two-dimensional space with a set of points (xi, yi) that satisfy xi < xj and yi > yj for all i < j. We want to have them all connected by a directed tree whose edges go toward either right (x positive) or upward (y positive). The figure below shows an example tree. 


Write a program that finds a tree connecting all given points with the shortest total length of edges.
Input
The input begins with a line that contains an integer n (1 <= n <= 1000), the number of points. Then n lines follow. The i-th line contains two integers xi and yi (0 <= xi, yi <= 10000), which give the coordinates of the i-th point.
Output
Print the total length of edges in a line.
Sample Input
5
1 5
2 4
3 3
4 2
5 1
1
10000 0
Sample Output
12
0
题意:给出平面上n个点,要求把这些点连起来构成一棵树连起来的路径最短,树只能向左和向右生长。感觉这是类似石子合并的一个区间dp,但是数据量有点大,所以要用到平行四边形优化(第一次写平行四边形优化的题目,竟然一次过,开心)。我感觉平行四边形优化,主要是证明出满足四边形不等式,然后会用就行了,平行四边形不等式为:w(i, j) + w(i1, j1) <= w(i1 , j) w(i ,j1),只要能证明出这个不等式就可以使用平行四边形优化。

确定了这一点。就是找动态转移方程了,dp[i][j]表示合并i到j所有的点之后树的最小长度

dp[i][j] = dp[i][k] + dp[k+1][j] + abs(no[k+1].x - no[i].x + no[k].y - no[j].y);


#include<iostream>
#include<stdio.h>
#include<string>
#include<math.h>
#include<queue>
#include<vector>
#include<string.h>
#include<algorithm>
#include<iterator>
using namespace std;
typedef unsigned long long ll;
const int inf = 0x3f3f3f3f ;
struct node{
    int x , y ;
}no[1005];
int n ;
int dp[1005][1005];
int s[1005][1005];
int main(){
    while(~scanf("%d" , &n)){
        for(int i = 1 ; i <= n ; i ++){
            scanf("%d %d" , &no[i].x , &no[i].y);
        }
        for(int i = 0 ; i <= n ; i ++){
            for(int j = 0 ; j <= n ; j ++){
                dp[i][j] = inf;
            }
        }
        for(int i = 0 ; i <= n ; i ++){
            s[i][i] = i ;
            dp[i][i] = 0;
        }

        for(int len = 1 ; len < n ; len ++){
            for(int i = 1 ; i <= n - len ; i ++){
                int j = i + len;
                //dp[i][j] = inf;
                for(int k =  s[i][j - 1] ; k <= s[i + 1][j] ; k ++){
                    if(dp[i][j] > dp[i][k] + dp[k+1][j] + abs(no[k+1].x - no[i].x + no[k].y - no[j].y)){
                        dp[i][j] = dp[i][k] + dp[k+1][j] + abs(no[k+1].x - no[i].x + no[k].y - no[j].y);
                        s[i][j] = k ;
                    }

                }
            }
        }
        cout<<dp[1][n]<<endl;
    }
    return 0;
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值