zoj 3537 凸包+三角剖分dp(切蛋糕)

题意:给出一些点表示多边形蛋糕的顶点的位置(如果蛋糕是凹多边形就不能切),切蛋糕时每次只能在顶点和顶点间切,每一次切蛋糕都有相应的代价,给出代价的公式为:cost(i, j) = |xi + xj| * |yi + yj| % p(其中p为输入里给定的一个常数)。问把蛋糕三角剖分的最小代价是多少?

思路:

首先判断多边形是凸的非常容易,只要求一遍凸包看看凸包的点数和给定的点数是不是一样多即可(可以有优化:如果有点弹出栈,那么必然不是凸多边形)

三角剖分问题是典型的dp问题。用dp[i][j]表示从i点到j点所构成的多边形的最优三角剖分,我们以(i,j)边为三角形的一边,那么三角形的另一个顶点就在i+1到j-1中。枚举这个点,然后求分开的两个小部分即可。显然可以从i和j之间的距离从小到大递推dp,也可以用记忆化搜索的递归形式(我的代码采用后者)。

因为任意多边形都有三角剖分,所以如果是凹的咋办?想了一下就用这种思路即可,只是每次枚举的时候判断一下该点是否成立(是否会经过多边形外的地方)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
using namespace std;
#define clr(s,t) memset(s,t,sizeof(s))
#define INF 0x3fffffff
#define N 305
struct point{
    int x,y;
}stack[N],s[N],start;
int n,p,dp[N][N],w[N][N];
int dist(point a,point b){
    return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
int multi(point a,point b,point c){
    return (b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x);
}
int cmp(point a,point b){
    int i = multi(start,a,b);
    if(i>0)
        return 1;
    if(i<0)
        return 0;
    return dist(start,a)<dist(start,b);
}
int convex(){
    int top = -1;
    sort(s,s+n,cmp);
    stack[++top] = start;
    stack[++top] = s[1];
    stack[++top] = s[2];
    for(int i = 3;i<n;i++){
        while(top>1 && multi(stack[top-1], stack[top], s[i])<0)
            return 0;
        stack[++top] = s[i];
    }
    return 1;
}
int solve(int a,int b){
    if(dp[a][b]!=-1)
        return dp[a][b];
    if(a+2 == b)
        return dp[a][b]=0;
    dp[a][b] = INF;
    dp[a][b] = min(dp[a][b], w[a+1][b]+solve(a+1, b));
    dp[a][b] = min(dp[a][b], w[a][b-1]+solve(a, b-1));
    for(int i = a+2;i<=b-2;i++)
        dp[a][b] = min(dp[a][b], w[a][i]+w[i][b]+solve(a, i)+solve(i, b));
    return dp[a][b];
}
int main(){
    while (scanf("%d %d",&n,&p) != EOF) {
        int i,j,now;
        clr(dp, -1);
        for(i = now = 0;i<n;i++){
            scanf("%d %d",&s[i].x,&s[i].y);
            if(s[i].y<s[now].y || (s[i].y==s[now].y && s[i].x<s[now].x))
               now = i;
        }
        start = s[now];
        if(!convex()){
            printf("I can't cut.\n");
            continue;
        }
        for(i = 0;i<n-1;i++)
            for(j = i+2;j<n;j++)
                w[i][j] = w[j][i] = abs((stack[i].x+stack[j].x)*(stack[i].y+stack[j].y))%p;
        printf("%d\n",solve(0,n-1));
    }
    return 0;
}


你可能感兴趣的:(zoj 3537 凸包+三角剖分dp(切蛋糕))