Codeforces 1146H. Satanic Panic(极角排序+DP)

Codeforces 1146H. Satanic Panic

题目大意

  • 给出平面内的 N N N个点,求多少种方案能选择五个点构成一个“五角星”。这里的“五角星”不一定要每条边相同,但要保证该线段之间该相交的地方要相交(脑补一下正常五角星的形状)。
  • N ≤ 300 N≤300 N300

题解

  • 首先可以想到转换一下题意,要求的是五角星,不太好统计,不妨可以把每个五角星对应成五个点顺次连接形成的凸包,也很显然每个五个点的凸包和五角星都是一一对应的,于是我们可以求这样的凸包数量。
  • 考虑到凸包每条边的极角是依次递增的,所以不妨先把边连出来,一共 N ( N − 1 ) N(N-1) N(N1)条边(顶点相同方向不同也算不同),接着按照它们对应的向量的极角进行排序,用DP求方案数。
  • f i , j , k f_{i,j,k} fi,j,k表示以 i i i为起点,凸包第 k k k个点是 j j j的方案数,显然有转移
  • f i , i , 0 = 1 f_{i,i,0}=1 fi,i,0=1
  • f i , j , k = ∑ ( j ′ , j ) ∈ E f i , j ′ , k − 1 f_{i,j,k}=sum_{(j',j)in E} f_{i,j',k-1} fi,j,k=(j,j)Efi,j,k1
  • 由于需要保证加入的每条边极角是递增的,所以要先枚举边再转移。

代码

#include<cstdio> 
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define N 310
#define ld long double
#define ll long long
struct node{
	int x, y;
	ld d;
}a[N], e[N * N];
ll f[N][N][6];
int cmp(node x, node y) {
	return x.d < y.d;
}
int main() {
	int n, i, j, l;
	scanf("%d", &n);
	for(i = 1; i <= n; i++) {
		scanf("%d%d", &a[i].x, &a[i].y);
	}
	int tot = 0;
	for(i = 1; i <= n; i++) 
		for(j = 1; j <= n; j++) if(i != j) {
			e[++tot].x = i, e[tot].y = j;
			e[tot].d = atan2(a[j].y - a[i].y, a[j].x - a[i].x);
		}
	sort(e + 1, e + tot + 1, cmp);
	for(i = 1; i <= n; i++) f[i][i][0] = 1;
	for(i = 1; i <= tot; i++) {
		for(j = 1; j <= n; j++) {
			for(l = 0; l < 5; l++) {
				f[j][e[i].y][l + 1] += f[j][e[i].x][l];
			}
		}
	}
	ll ans = 0;
	for(i = 1; i <= n; i++) ans += f[i][i][5];
	printf("%lld", ans);
	return 0;
}
哈哈哈哈哈哈哈哈哈哈
原文地址:https://www.cnblogs.com/LZA119/p/13910028.html