ZOJ 3537 Cake (凸包+三角形剖分+区间DP)

Cake

Time Limit: 1 Second      Memory Limit: 32768 KB

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 算切线的费用,问最少的切割费用。
思路:先求凸包,再求出各个相切的费用,接着就是区间DP求最小花费了,dp[i][j]为以i为起点,j为终点的凸包被切割成一个个小三角形所需要的费用
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;

const double EP  = 1E-9;
const double PI  = acos(-1.0);

int sgn(double x)
{
    if(fabs(x) < EP)return 0;
    if(x < 0)return -1;
    else return 1;
}
/* 基本几何结构 */
struct POINT
{
    double x,y;
    POINT(double a=0, double b=0)
    {
        x=a; y=b;   //constructor
    }
    void input()
    {
        scanf("%lf%lf", &x, &y);
    }
    POINT operator -(const POINT &b)const
    {
        return POINT(x - b.x,y - b.y);
    }
    double operator ^(const POINT &b)const//叉积
    {
        return x*b.y - y*b.x;
    }
    double operator *(const POINT &b)const//点积
    {
        return x*b.x + y*b.y;
    }
};
double dist(POINT p1,POINT p2)                // 返回两点之间欧氏距离
{
    return( sqrt( (p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y) ) );
}
/*
* 求凸包,Graham算法
* 点的编号0~n-1
* 返回凸包结果Stack[0~top-1]为凸包的编号
*/
const int MAXN = 305;
POINT list[MAXN];
int Stack[MAXN],top;
//相对于list[0]的极角排序
bool _cmp(POINT p1,POINT p2)
{
    double tmp = (p1-list[0])^(p2-list[0]);
    if(sgn(tmp) > 0)return true;
    else if(sgn(tmp) == 0 && sgn(dist(p1,list[0]) - dist(p2,list[0])) <= 0)
        return true;
    else return false;
}
void Graham(int n)
{
    POINT p0;
    int k = 0;
    p0 = list[0];
//找最下边的一个点
    for(int i = 1; i < n; i++)
    {
        if( (p0.y > list[i].y) || (p0.y == list[i].y && p0.x > list[i].x) )
        {
            p0 = list[i];
            k = i;
        }
    }
    swap(list[k],list[0]);
    sort(list+1,list+n,_cmp);
    if(n == 1)
    {
        top = 1;
        Stack[0] = 0;
        return;
    }
    if(n == 2)
    {
        top = 2;
        Stack[0] = 0;
        Stack[1] = 1;
        return ;
    }
    Stack[0] = 0;
    Stack[1] = 1;
    top = 2;
    for(int i = 2; i < n; i++)
    {
        while(top > 1 && sgn((list[Stack[top-1]]-list[Stack[top-2]])^(list[i]-list[Stack[top-2]])) <=0)
            top--;
        Stack[top++] = i;
    }
}
int n,m;
int dist2(POINT p1,POINT p2)        //这里我为了方便就强转,好像没错
{
    return( abs((int)p1.x+(int)p2.x)*abs((int)p1.y+(int)p2.y) % m );
}
int cost[MAXN][MAXN],dp[MAXN][MAXN];
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=0; i<n; i++)
            list[i].input();
        Graham(n);
        if(top < n)
        {
            puts("I can't cut.");
            continue;
        }
        memset(cost,0,sizeof(cost));
        for(int i=0; i<n; i++)
            for(int j=i+2; j<n; j++)
            {
                cost[i][j] = cost[j][i] = dist2(list[Stack[i]],list[Stack[j]]);
            }
        for(int i=0; i<n; i++)
            for(int j=0; j<n; j++)
            {
                dp[i][j] = 1000000000;
                dp[i][(i+1)%n] = 0;
            }
        for(int len=2; len<n; len++)
            for(int i=0; i+len<n; i++)
            {
                int j=i+len;
                for(int k=i+1; k<=j-1; k++)
                {
                    dp[i][j] = min(dp[i][j],dp[i][k]+dp[k][j]+cost[i][k]+cost[k][j]);
                }
            }
        printf("%d\n",dp[0][n-1]);
    }
    return 0;
}
View Code

 

posted @ 2015-08-20 15:58  Doli  阅读(600)  评论(0编辑  收藏  举报