Uva1331最优三角剖分

题意:
输入一个多边形,找一个最大三角形面积最小的三角剖分,输出最大三角形的面积。

思路:区间DP

dp[i][j]表示从i点到j点的最优值,枚举中间点k。
转移方程为dp[i][j]=min(dp[i][j],max(area(i,j,k),max(dp[i][k],dp[k][j])))。

注意需要检验选的这个 k 是否有效,如果i-k或j-k两个对角线的任意一条与其他边相交,那么这个k就不能选。
怎么检查呢?选定一个k的时候,只需要看i,k,j三点组成的三角形内部是否有点,如果有,那么必然会相交。
怎么检查内部有点呢?如果一个点 o 在三角形内部,那么将它与三角形三个顶点连线一定可以把三角形切分成3个三角形,且3个三角形面积之和就是这个大三角形的面积,我们就是根据面积之和这一点来检验的。
http://www.cnblogs.com/Konjakmoyu/p/4905563.html

#include <cstdio>
#include <cstring>
#include <algorithm>
#define esp 1e-6
using namespace std;
const int INF = 1<<30;
const int N = 50+5;

int x[N], y[N], n;
double d[N][N];

// 向量叉乘求三角形面积 
double area(int a, int b, int c){
	double s = (double)(1.0/2)*(x[a]*y[b] + x[b]*y[c] + x[c]*y[a] - x[a]*y[c] - x[b]*y[a] - x[c]*y[b]);
	if(s < 0) return -s;
	return s;
}

// 检查三角形内部是否有点 
bool check(int a, int b, int c){
	for(int i = 0; i < n; ++i){
		if(i==a || i==b || i==c) continue;
		double s = area(a,b,i) + area(a,c,i) + area(b,c,i) - area(a,b,c);
		if(s < 0) s = -s;
		if(s < 0.01) return 0;
	}
	return 1;
}
// 记忆化搜索
double f(int i, int j){
	double& ans = d[i][j];
	if(i+1 == j) return ans = 0.0;
	if(ans > esp) return ans;
	ans = INF;
	for(int k = i+1; k < j; ++k){
		if(check(i,k,j))
			ans = min(ans, max(max(f(i,k), f(k,j)), area(i,k,j))); 
	}
	//printf("%lf\n",ans); 
	return ans;
}

int main()
{

	//freopen("in.txt","r",stdin);
	int T; scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		for(int i = 0; i < n; ++i) scanf("%d %d",&x[i],&y[i]);
		memset(d,0,sizeof(d));
		// 两种递推
		
		/*
		for(int len = 2; len < n; ++len){ // 枚举区间长度
		 	
			for(int i = 0; i+len < n; ++i){ // 枚举区间左端点 
				int j = i + len;
				d[i][j] = INF;
				d[i][i+1] = 0.0;
				
				for(int k = i+1; k < j; ++k){ // 枚举中间点 
					if(check(i,k,j))
						d[i][j] = min(d[i][j], max(max(d[i][k], d[k][j]), area(i,k,j))); 
				}
			}
		}
		*/		
		
		for(int j = 2; j < n; ++j){ // 枚举区间右端点 
		 	
			for(int i = j-1; i >= 0; --i){ // 枚举区间左端点 
				//int j = i + len;
				d[i][j] = INF;
				d[i][i+1] = 0.0;
				
				for(int k = i+1; k < j; ++k){ // 枚举中间点 
					if(check(i,k,j))
						d[i][j] = min(d[i][j], max(max(d[i][k], d[k][j]), area(i,k,j))); 
				}
			}
		}
		
		//f(0,n-1);  
		double ans = d[0][n-1]; 
		printf("%.1lf\n",ans);
	}

	return 0;
}


猜你喜欢

转载自blog.csdn.net/CY05627/article/details/88398416
今日推荐