Cake ZOJ - 3537(判断凸包模板+最优三角剖分+区间dp详解)

Cake ZOJ - 3537

 You want to hold a party. Here's a polygon-shaped cake on the table. You'd like to cut the cake into several triangle-shaped parts for the invited comers. You have a knife to cut. The trace of each cut is a line segment, whose two endpoints are two vertices of the polygon. Within the polygon, any two cuts ought to be disjoint. Of course, the situation that only the endpoints of two segments intersect is allowed.

The cake's considered as a coordinate system. You have known the coordinates of vexteces. Each cut has a cost related to the coordinate of the vertex, whose formula is costi, j = |xi + xj| * |yi + yj| % p. You want to calculate the minimum cost.

NOTICE: input assures that NO three adjacent vertices on the polygon-shaped cake are in a line. And the cake is not always a convex.

Input

There're multiple cases. There's a blank line between two cases. The first line of each case contains two integers, N and p (3 ≤ N, p ≤ 300), indicating the number of vertices. Each line of the following N lines contains two integers, x and y (-10000 ≤ x, y ≤ 10000), indicating the coordinate of a vertex. You have known that no two vertices are in the same coordinate.

Output

If the cake is not convex polygon-shaped, output "I can't cut.". Otherwise, output the minimum cost.

Sample Input

3 3
0 0
1 1
0 2

Sample Output

0

题意:

给定n个点的坐标,先问这些点是否能组成一个凸包,如果是凸包,问用不相交的线来切这个凸包使得凸包只由三角形组成,根据 cost(i, j) = |xi + xj| * |yi + yj| % p 算切线的费用,问最少的切割费用。

分析:

首先当然是要先判断是否是凸包了

因为之前没有写过凸包的题目所以这里就写一下判断凸包的算法


Andrew算法:

首先把所有点按照x从小到大排序,(如果x相同,按照y从小到大排序),删除重复点后得到的序列
p 1 , p 2 , . . . . 然后把 p 1 , p 2 放到凸包中,相当于入栈。从 p 3 开始,当新点在凸包“前进方向的左侧时继续即新点在栈顶点的左侧时入栈,如果在右侧,就依次删除最近加入的点,即删除栈顶,直到新点在栈顶左侧(有点像单调栈的操作)

如下图所示,新点 p 18 在向量 p 10 p 15 的右边(当前前进方向),因此需要从栈中删除 p 10 , p 15 ,让 p 8 的下一个点为 p 18 .重复这个过程,知道碰到最右边的 p n ,就求出了下凸包。

这里写图片描述
然后再反过来从 p n 开始再做一次,求出上凸包,合并起来就是完整的凸包了
这里写图片描述

struct Point{
    int x,y;
    Point(){}
    Point(int xx,int yy):x(xx),y(yy){}
    void read(){
        scanf("%d%d",&x,&y);
    }
    bool operator < (const Point &other)const{
        if(x == other.x)
            return y < other.y;
        return x < other.x;
    }
    Point operator - (const Point &other)const{
        return Point(x - other.x,y - other.y);
    }
};
Point p[400],ch[400];
int n;
double Cross(Point A,Point B){
    return A.x * B.y - A.y * B.x;
}
int ConvexHull(){
    sort(p,p+n);
    int cnt = 0;
    for(int i = 0; i < n; i++){
        while(cnt > 1 && Cross(ch[cnt-1] - ch[cnt-2],p[i] - ch[cnt-2]) <= 0) cnt--;
        ch[cnt++] = p[i];
    }
    int k = cnt;
    for(int i = n-2; i >= 0; i--){
        while(cnt > k && Cross(ch[cnt-1] - ch[cnt-2],p[i] - ch[cnt-2]) <= 0) cnt--;
        ch[cnt++] = p[i];
    }
    if(n > 1) cnt--;
    return cnt;
}

介绍完了凸包我们再来看看这道题怎么做呢?

需要用到最优三角剖分:


最优三角剖分:

对于一个n个顶点的凸多边形,有很多种方法可以对它进行三角剖分,即用n-3条互不相交的对角线把凸多边形分成n-2个三角形。为每个三角形规定一个权值函数w(i,j,k),求让所有三角形权和最大的方案。

定义dp[i][j]为子多边形i,i+1,….j-1,j(i < j)的最优值,则边i-j在最优解中一定对应一个三角形i-k-j,其中(i < k < j)那么同理,在i-k间也一定能找到一个最优值,k-j之间也可以找到一个最优值,一次类推

如图:
这里写图片描述
因此状态转移方程为

d p [ i ] [ j ] = m a x ( d p [ i ] [ j ] , d p [ i ] [ k ] + d p [ k ] [ j ] + w ( i , j , k ) )

时间复杂度 O ( n 3 ) ,问题的解为 d p [ 0 ] [ n 1 ]


那么对于这道题就很明确了

状态转移方程为:

d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , d p [ i ] [ k ] + d p [ k ] [ j ] + c o s t [ i ] [ k ] + c o s t [ k ] [ j ] )

三重for循环枚举ijk,即可,区间dp

code:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
int m,f[400][400],dp[400][400];
struct Point{
    int x,y;
    Point(){}
    Point(int xx,int yy):x(xx),y(yy){}
    void read(){
        scanf("%d%d",&x,&y);
    }
    bool operator < (const Point &other)const{
        if(x == other.x)
            return y < other.y;
        return x < other.x;
    }
    Point operator - (const Point &other)const{
        return Point(x - other.x,y - other.y);
    }
};
Point p[400],ch[400];
int n;
double Cross(Point A,Point B){
    return A.x * B.y - A.y * B.x;
}
int ConvexHull(){
    sort(p,p+n);
    int cnt = 0;
    for(int i = 0; i < n; i++){
        while(cnt > 1 && Cross(ch[cnt-1] - ch[cnt-2],p[i] - ch[cnt-2]) <= 0) cnt--;
        ch[cnt++] = p[i];
    }
    int k = cnt;
    for(int i = n-2; i >= 0; i--){
        while(cnt > k && Cross(ch[cnt-1] - ch[cnt-2],p[i] - ch[cnt-2]) <= 0) cnt--;
        ch[cnt++] = p[i];
    }
    if(n > 1) cnt--;
    return cnt;
}
int calc(Point a,Point b){
    return (abs(a.x + b.x) * abs(a.y + b.y)) % m;
}
int main(){
    while(scanf("%d%d",&n,&m) != EOF){
        memset(p,0,sizeof(p));
        memset(ch,0,sizeof(ch));
        memset(f,0,sizeof(f));
        for(int i = 0; i < n; i++){
            p[i].read();
        }
        if(n == 3){
            puts("0");
            continue;
        }
        if(ConvexHull() < n){
            printf("I can't cut.\n");
        }
        else{
            for(int i = 0; i < n; i++){
                for(int j = i+2; j < n; j++){
                    f[i][j] = f[j][i] = calc(ch[i],ch[j]);
                }
            }
            for(int i = 0; i < n; i++){
                for(int j = 0; j < n; j++){
                    dp[i][j] = INF;
                }
                dp[i][(i+1)%n] = 0;
            }
            for(int i = n-3; i >= 0; i--){
                for(int j = i+2; j < n; j++){
                    for(int k = i+1; k < j; k++){
                        dp[i][j] = min(dp[i][j],dp[i][k]+dp[k][j]+f[i][k]+f[k][j]);
                    }
                }
            }
            printf("%d\n",dp[0][n-1]);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/codeswarrior/article/details/81327277
今日推荐