题目:ZOJ-3537
题目大意:
给定n个点的坐标,先问这些点是否能组成一个凸包,如果是凸包,问用不相交的线来切这个凸包使得凸包只由三角形组成,根据 cost(i, j) = |xi + xj| * |yi + yj| % p 算切线的费用,问最少的切割费用。
思路:先判断多边形是否是凸包,,,
凸包:和凸多边形差不多的意思,,
如何判断凸多边形:http://blog.csdn.net/kavu1/article/details/50772428
这里讲一下最简单的方法:
利用以当前顶点为中心的矢量叉乘或者计算三角形的有符号面积判断多边形的方向以及当前顶点的凹凸性。
假设当前连续的三个顶点分别是P1,P2,P3。计算向量(P1,P2),(P1,P3)的叉乘,也就是计算三角形P1P2P3的面积,得到的结果如果大于0,则表示P2点在线段P1和P3的右侧,多边形的顶点是逆时针序列。然后依次计算下一个前后所组成向量的叉乘,如果在计算时,出现负值,则此多边形时凹多边形,如果所有顶点计算完毕,其结果都是大于0,则多边形时凸多边形。
代码:
struct point { int x,y; friend bool operator < (const point &a,const point &b) { if (a.y == b.y) return a.x < b.x; return a.y < b.y; } } src[MAXN]; int cost[MAXN][MAXN]; int N,P; int dp[MAXN][MAXN]; point save[MAXN],tmp[MAXN]; int cross(point p0,point p1,point p2) { //cout<<"p0="<<p0.x<<" "<<p0.y<<" p1="<<p1.x<<" "<<p1.y<<" p2="<<p2.x<<" "<<p2.y<<endl; return (p1.x - p0.x) * (p2.y - p0.y) - (p1.y - p0.y) * (p2.x - p0.x); } int graphm(point * p,int n)//0-n-1的n个点,放在结构体里面,排序后按照逆序判断各点的凹凸性 { sort(p,p + n); save[0] = p[0]; save[1] = p[1]; int top = 1; for(int i = 0 ; i < n ; i++) { while(top && cross(save[top],p[i],save[top-1]) >= 0) { //cout<<"top="<<top<<" i="<<i<<" top-1="<<top-1<<endl; top--; } save[++top] = p[i]; } int mid = top; for(int i = n - 2 ; i >= 0 ; i--) { while(top > mid && cross(save[top],p[i],save[top-1]) >= 0) { //cout<<"top="<<top<<" i="<<i<<" top-1="<<top-1<<endl; top--; } save[++top]=p[i]; } return top; }求出凸包后,再求出各个相切的费用,接着就是区间DP求最小花费了,dp[i][j]为以i为起点,j为终点的凸包被切割成一个个小三角形所需要的费用
完整代代码:
#include <iostream> #include <cmath> #include <algorithm> #include <cstring> #include <string> #include <vector> #include <cstdio> #include <set> using namespace std; typedef long long ll; const int MAXN = 310; struct point { int x,y; friend bool operator < (const point &a,const point &b) { if (a.y == b.y) return a.x < b.x; return a.y < b.y; } } src[MAXN]; int cost[MAXN][MAXN]; int N,P; int dp[MAXN][MAXN]; point save[MAXN],tmp[MAXN]; int cross(point p0,point p1,point p2) { //cout<<"p0="<<p0.x<<" "<<p0.y<<" p1="<<p1.x<<" "<<p1.y<<" p2="<<p2.x<<" "<<p2.y<<endl; return (p1.x - p0.x) * (p2.y - p0.y) - (p1.y - p0.y) * (p2.x - p0.x); } int graphm(point * p,int n) { sort(p,p + n); save[0] = p[0]; save[1] = p[1]; int top = 1; for(int i = 0 ; i < n ; i++) { while(top && cross(save[top],p[i],save[top-1]) >= 0) { //cout<<"top="<<top<<" i="<<i<<" top-1="<<top-1<<endl; top--; } save[++top] = p[i]; } int mid = top; for(int i = n - 2 ; i >= 0 ; i--) { while(top > mid && cross(save[top],p[i],save[top-1]) >= 0) { //cout<<"top="<<top<<" i="<<i<<" top-1="<<top-1<<endl; top--; } save[++top]=p[i]; } return top; } int getcost(point a,point b) { return (abs(a.x + b.x) * abs(a.y+b.y)) % P; } int main() { while (scanf("%d%d",&N,&P) != EOF) { for (int i = 0 ; i < N ; i++) scanf("%d%d",&src[i].x,&src[i].y); int num = graphm(src,N); if (num < N) { printf("I can't cut.\n"); } else { 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] = getcost(save[i],save[j]); } memset(dp,0x3f,sizeof(dp)); for (int i = 0 ; i < N ; i++) dp[i][(i + 1) % N] = 0;//相邻两点费用为0 for(int i=N-3;i>=0;i--)//从倒数第二个点开始枚举 { for(int j=i+2;j<N;j++) { 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]); } } } printf("%d\n",dp[0][N - 1]); } } return 0; }