ZOJ - 3537 Cake(凸包+區間dp)

傳送門

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.

<h4< dd=""> 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邊形分成n-2個三角形所需要的最小花費。

首先題目已經說了必須是凸多邊形才能被分割,所以要先判斷凸多邊形(判斷凸多邊形其實就是套模板了),然後就是明顯的區間dp了。

dp[i][j]爲以i爲起點,j爲終點的凸包被切割成一個個小三角形所需要的費用,狀態轉移方程爲:
dp[i][j] = min(dp[i][j],dp[i][k] + dp[k][j] + cost[i][k] + cost[k][j]);

#include<stdio.h>
#include<iostream>
#include<set>
#include<queue>
#include<algorithm>
#include<math.h>
#include<string.h>
#include<string>
#include<vector>
using namespace std;
typedef long long ll;
#define eps 1e-9
const int inf = 0x3f;
int cost[305][305];
int dp[305][305];
int n , p;
const int MAXN=305;

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

int cross(point p0,point p1,point p2)
{
    return (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x);
}
double dis(point p1,point p2)
{
    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(po[0],p1,p2);
    if(tmp>0) return true;
    else if(tmp==0&&dis(po[0],p1)<dis(po[0],p2)) return true;
    else return false;
}
void init(int n)
{
    int i,k;
    point p0;
    scanf("%d%d",&po[0].x,&po[0].y);
    p0.x=po[0].x;
    p0.y=po[0].y;
    k=0;
    for(i=1;i<n;i++)
    {
        scanf("%d%d",&po[i].x,&po[i].y);
        if( (p0.y>po[i].y) || ((p0.y==po[i].y)&&(p0.x>po[i].x)) )
        {
            p0.x=po[i].x;
            p0.y=po[i].y;
            k=i;
        }
    }
    po[k]=po[0];
    po[0]=p0;
    sort(po+1,po+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(po[stack[top-1]],po[stack[top]],po[i])<=0) top--;
            top++;
            stack[top]=i;
        }
    }
}//判斷凸包

int main()
{
    while(~scanf("%d%d" , &n , &p)){
        init(n);
        graham(n);
        if(top != n - 1){
            cout<<"I can't cut."<<endl;
            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] = abs(po[i].x + po[j].x)*abs(po[i].y + po[j].y)%p;
            }
        }
        memset(dp , inf , sizeof(dp));
        for(int i = 0 ; i < n ; i ++)
            dp[i][i+1] = 0;
        for(int len = 2 ; len <= n ; len ++){
            for(int i = 0 ; i < n-len ; i ++){
                int j = len + i;
                for(int k = i + 1 ; k < j ; k++)
                    dp[i][j] = min(dp[i][j],dp[i][k] + dp[k][j] + cost[i][k] + cost[k][j]);
            }
        }
        cout<<dp[0][n-1]<<endl;
    }
    return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章