• 题解 LA4285


    题目大意 多组数据,每组数据给定一个正整数 (n(nleq 50)) 和平面内 (n) 个点,求出有多少个由这些点组成的简单,单调(monotone)多边形。一个单调多边形指的是构成这个多边形的所有边的斜率都在 ((-frac{pi}2,frac{pi}2)) 中。数据保证没有两个点 (x) 轴坐标相同。

    题目图片

    分析 不难想到将所有点按照 (x) 坐标排个序,然后按照上下两条折线考虑。我们令 (f[i][j](i eq j or i=j=1 or i=j=n)) 为上折线最右端为第 (i) 个点,下折线最右端为第 (j) 个点的所有方案数。不妨假定 (i<j),则 (f[i][j]) 只能向 (f[i][j+1])(f[j+1][j]) 转移。向 (f[i][j+1]) 的转移一定是可以的,我们来考虑向 (f[j+1][j]) 的转移。

    用数学归纳法可以得到 (f[i][j]) 的所有方案数一定满足单调,那么就只用考虑简单了,也就是说边 ((i,j+1)) 不能和边 ((k, k+1)|i<k<j) 相交,也就是说点 (k|i<kleq j) 全都在边 ((i,j+1)) 的同一侧(下侧),更进一步,如果考虑点 (j+1) 的极角,有 (ang_i<ang_k(i<kleq j))。那么具体实现就很简单了。

    最后要考虑到点 (n) 的转移,只能用 (f[i][n-1]) 或者 (f[n-1][j]) 来转移,那么分别用极角判定就可以了。

    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn = 55;
    const double PI = acos(-1);
    
    int T, n;
    int f[maxn][maxn];
    struct Point {
    	int x, y;
    	inline bool operator<(Point d) const { return x < d.x; }
    } p[maxn];
    
    int main()
    {
    	scanf("%d", &T);
    	while(T--) {
    		memset(f, 0, sizeof f);
    		
    		scanf("%d", &n);
    		for(int i = 1; i <= n; ++i)
    			scanf("%d%d", &p[i].x, &p[i].y);
    		
    		sort(p + 1, p + n + 1);
    		
    		f[1][1] = 1;
    		for(int i = 1; i < n - 1; ++i) {
    			for(int j = 1; j < n - 1; ++j) {
    				if(i == j && i != 1) continue;
    				
    				int now = max(i, j) + 1, pos;
    				bool whi = now == j + 1, flag = false;
    				
    				if(!whi) pos = j, f[now][j] += f[i][j];
    				else pos = i, f[i][now] += f[i][j];
    				
    				double ang = atan2(p[pos].y - p[now].y, p[pos].x - p[now].x);
    				if(ang < 0) ang += 2 * PI;
    				
    				for(int k = min(i, j) + 1; k < now; ++k) {
    					double tmp = atan2(p[k].y - p[now].y, p[k].x - p[now].x);
    					
    					if(tmp < 0) tmp += 2 * PI;
    					
    					if((whi && tmp <= ang) || (!whi && tmp >= ang)) {
    						flag = 1;
    						break;
    					}
    				}
    				
    				if(!flag) {
    					if(!whi) f[i][now] += f[i][j];
    					else f[now][j] += f[i][j]; 
    				}
    			}
    		}
    		
    		double ang = atan2(p[n - 1].y - p[n].y, p[n - 1].x - p[n].x);
    		
    		if(ang < 0) ang += 2 * PI;
    		for(int i = n - 2; i >= 1; --i) {
    			double tmp = atan2(p[i].y - p[n].y, p[i].x - p[n].x);
    			if(tmp < 0) tmp += 2 * PI;
    			
    			if(tmp < ang) f[n][n] += f[i][n - 1];
    			
    			ang = min(ang, tmp);
    		}
    		
    		ang = atan2(p[n - 1].y - p[n].y, p[n - 1].x - p[n].x);
    		
    		if(ang < 0) ang += 2 * PI;
    		for(int i = n - 2; i >= 1; --i) {
    			double tmp = atan2(p[i].y - p[n].y, p[i].x - p[n].x);
    			if(tmp < 0) tmp += 2 * PI;
    			
    			if(tmp > ang) f[n][n] += f[n - 1][i];
    			
    			ang = max(ang, tmp);
    		}
    		
    		printf("%d
    ", f[n][n]);
    	}
    }
    
  • 相关阅读:
    android窗口管理剖析
    android Activity管理简析
    LeetCode第三十四题-寻找数组中对应目标值的首尾索引
    LeetCode第三十三题-判断字符串中最长的有效括号数
    LeetCode第三十二题-判断字符串中最长的有效括号数
    LeetCode第三十一题-下一个排列
    LeetCode第三十题-字符串中具有所有单词串联的子串
    LeetCode第二十九题-整数除法
    LeetCode第二十八题-判断字符串是否包含子字符串
    LeetCode第二十七题-删除数组指定元素
  • 原文地址:https://www.cnblogs.com/whx1003/p/12387818.html
Copyright © 2020-2023  润新知