题意:给出一些点表示多边形蛋糕的顶点的位置(如果蛋糕是凹多边形就不能切),切蛋糕时每次只能在顶点和顶点间切,每一次切蛋糕都有相应的代价,给出代价的公式为: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; }