zoj3537cake【最优三角剖分+凸包】

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

几乎就是照着敲的,惭愧ing

说题意,想找论文题没找到,只有浙大的这个比较类似,但是有凸包,本宝宝不开心……简单说一下凸包,“在地上放置一些不可移动的木桩,用一根绳子把他们尽量紧地圈起来,并且为凸边形,这就是凸包了”,本题要求所有已知点都在绳子上,不在的输出提示信息。什么情况不在,凸包的算法入栈个数小于点的个数的时候不在。

再说排序之后的处理,根据论文中对于最优三角剖分的处理讲解,我们知道需要确定一个凸多边形,然后找一条边把他分成两半。实际写的时候受前几个题的影响,后面的花费写错了两个地方

dp[i][k]+dp[k][j]+cost[i][k]+cost[k][j] 1.后一个dp也是从k开始  2.cost是两个值,因为算法是在i-j的区间内找到一个过渡点k,连接i-k j-k形成中间三角形和两个凸多边形而成

再有就是注意循环顺序,这个题不用处理环,但是最好是开头定点从大往小枚举

/****************
zoj3537
2016.2.19
C++ 	70 	8096
***************/
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int MAXN=1000;

struct point
{
    int x,y;
};
point list[MAXN];
int stack[MAXN],top;

int cross(point p0,point p1,point p2) //计算叉积  p0p1 X p0p2
{
    return (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x);
}
double dis(point p1,point p2)  //计算 p1p2的 距离
{
    return sqrt((double)(p2.x-p1.x)*(p2.x-p1.x)+(p2.y-p1.y)*(p2.y-p1.y));
}
bool cmp(point p1,point p2) //极角排序函数 , 角度相同则距离小的在前面
{
    int tmp=cross(list[0],p1,p2);
    if(tmp>0) return true;
    else if(tmp==0&&dis(list[0],p1)<dis(list[0],p2)) return true;
    else return false;
}
void init(int n) //输入,并把  最左下方的点放在 list[0]  。并且进行极角排序
{
    int i,k;
    point p0;
    scanf("%d%d",&list[0].x,&list[0].y);
    p0.x=list[0].x;
    p0.y=list[0].y;
    k=0;
    for(i=1;i<n;i++)
    {
        scanf("%d%d",&list[i].x,&list[i].y);
        if( (p0.y>list[i].y) || ((p0.y==list[i].y)&&(p0.x>list[i].x)) )
        {
            p0.x=list[i].x;
            p0.y=list[i].y;
            k=i;
        }
    }
    list[k]=list[0];
    list[0]=p0;

    sort(list+1,list+n,cmp);
}

void graham(int n)
{
    int i;
    if(n==1) {top=0;stack[0]=0;}
    if(n==2)
    {
        top=1;
        stack[0]=0;
        stack[1]=1;
    }
    if(n>2)
    {
        for(i=0;i<=1;i++) stack[i]=i;
        top=1;

        for(i=2;i<n;i++)
        {
            while(top>0&&cross(list[stack[top-1]],list[stack[top]],list[i])<=0) top--;
            top++;
            stack[top]=i;///0~n-1
        }
    }
}
void print(int n)
{
    for(int i=0;i<n;i++)
        printf("stack=%d,x=%d,y=%d\n",stack[i],list[stack[i]].x,list[stack[i]].y);
}
int cost[MAXN][MAXN];
int calc(point p1,point p2,int m)//计算题目定义的cost
{
    return abs(p1.x+p2.x)*abs(p1.y+p2.y)%m;
}
int dp[MAXN][MAXN];
int main()
{
    freopen("cin.txt","r",stdin);
    int p,n;
    while(~scanf("%d%d",&n,&p))
    {
        init(n);
        graham(n);
        //print(n);
        if(top!=n-1)
        {
            printf("I can't cut.\n");
            continue;
        }
        memset(cost,0,sizeof(cost));
        for(int i=0;i<n;i++)
            for(int j=i+2;j<n;j++)
                cost[j][i]=cost[i][j]=calc(list[i],list[j],p);
        for(int i=0;i<n;i++)
        {
            for(int j=i;j<n;j++) dp[i][j]=0x3f3f3f3f;
            dp[i][(i+1)%n]=0;
        }
        for(int i=n-3;i>=0;i--)//start
        {
            for(int j=i+2;j<n;j++)//end
            {
                for(int k=i+1;k<j;k++)//mid
                dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]+cost[i][k]+cost[k][j]);
            }
        }
//        for(int l=2;l<n;l++)//len
//        {
//            for(int i=0;i<=n-3;i++)//start
//            {
//                int j=i+l;
//                for(int k=i+2;k<j;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;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值