题意给2n个点,问把他们两两连接有多少种方案,线段不允许交叉。
哎,感觉我好水。。。打了两天CF,但是笔记本掉帧太厉害,没兴致玩了。明天来鸡房呆着敲代码吧。。。
假设有p个点,当然题目给了n表达的是2n,所以p一定是偶数。顺序标号后发现,只需要考虑第一个点和谁连接就好了,因为有个线段不能相交的条件,所以一旦确定了第一个点连出的线段,这p个点就被这线段分成了两部分。很容易知道每部分的点数必须为偶数,否则一定产生交点。那么就很简单了,只需要考虑第一个点和第2,4,6,8,...,p连接的情况就可以了,定义dp[i]表示i个点的方案数,那么:
dp[i] = dp[i-2] + dp[2]*dp[i-4] + dp[4]*dp[i-6] + ... + dp[n-2]
为了表达方便,初始化dp[0]为1,那么上述转移就是:dp[i] = dp[j]*dp[i-2-j] (j=0,2,4,...,i-2)
C++代码:
#include<cstdio>
typedef long long ll;
const int MAX = 202;
ll dp[MAX] = {1, 0, 1};
int main() {
for (int i = 4; i < MAX; i += 2) {
for (int j = 2; j <= i; j += 2) {
dp[i] += dp[i-j] * dp[j-2];
}
}
int n;
while (~scanf(" %d", &n) && (~n)) {
printf("%lld\n", dp[n<<1]);
}
return 0;
}
当然,这是很糟糕的代码:点数达到一定量后溢出long long了,所以还是大整数(Java版):
import java.util.*;
import java.math.*;
public class Main {
public static void main(String args[]) {
BigInteger []dp = new BigInteger[202];
dp[0] = BigInteger.valueOf(1);
dp[2] = BigInteger.valueOf(1);
for (int i = 4; i < 202; i += 2) {
dp[i] = BigInteger.ZERO;
for (int j = 2; j <= i; j += 2) {
dp[i] = dp[i].add(dp[i-j].multiply(dp[j-2]));
//System.out.println("dp[" + i + "] = " + dp[i]);
}
}
Scanner cin = new Scanner(System.in);
int n;
while (cin.hasNextInt()) {
n = cin.nextInt();
if (n == -1) break;
System.out.println(dp[n<<1]);
}
}
}
好吧,我狠无聊,好想打字...只是缓解一下两天没有敲代码的手瘾...吃翔去了╮(╯▽╰)╭