lzp穿鞋带的理论方法
发布时间: 2017年12月11日 14:01 最后更新: 2017年12月11日 14:46 时间限制: 1000ms 内存限制: 128M
对于鞋,lzp有一套奇怪的穿鞋带理论:
0.鞋带孔之间的鞋带不能是弯的,即鞋带穿过两鞋带孔时必须绷直。
1.鞋带必须从最前面的一头左端穿入,并从最前面右端穿出,如图所示为一些普qi通pa的穿鞋带方法,○表示鞋带孔,直线表示鞋带。


2.穿鞋带时,必须左右交替,如上图所示
3.穿完的鞋带必须存在m个以上的交点,上图左边的共有四个交点,右边的有九个交点。lzp的鞋保证所有交点都只被鞋带经过两次。
现在,lzp想知道,对于每只纵向排列着n对鞋带孔的鞋,有几种穿鞋带方案符合他的理论(上图中n=5)
第一行输入组数T(T<=10)
每组数据输入两个数n(1<=n<=7),m(0<=m<=1000)
对于每组数据输出一行,符合lzp的理论的穿鞋带方案数
7 5 4 5 9 3 2 1 0 3 6 7 10 6 20
576 540 4 1 1 518269 10221
题目分析 : 每次只能重一个点穿过,并且左右交替,点数不多 最多只有7个,那不就是暴力吗,想想要怎么暴力,每次可以交替枚举出所有可能的边,枚举出所有可能的边以后,
在判断当前的这些边总共有多少个交点,怎么判断两个点是不是相交呢,如果给两列点瞬时标上号,那么(a[1]-a[2])*(b[1]-b[2]) < 0,则说明两条线是相交的,自己可以举几个例子验证一下。
代码示例 :
const int eps = 1e6+5;
const double pi = acos(-1.0);
const int inf = 1<<29;
#define Max(a,b) a>b?a:b
#define Min(a,b) a>b?b:a
#define ll long long
int p2[20];
int p1[20];
struct node
{
int a, b;
}pre[eps];
int sign = 1;
int p;
ll ans = 0;
int n, m;
bool check(){
int k = 1;
for(int i = 2; i <= n; i++){
pre[k].a = i;
pre[k++].b = p1[i];
}
for(int i = 1; i <= n; i++){
pre[k].a = p2[i];
pre[k++].b = i;
}
//printf("k = %d
", k);
int cnt = 0;
for(int i = 1; i < k; i++){
for(int j = i+1; j < k; j++){
if ((pre[i].a - pre[j].a)*(pre[i].b - pre[j].b) < 0){
cnt++;
}
}
}
if (cnt >= m) return true;
else return false;
}
void find(int x){
for(int i = 2; i <= n; i++){
//if (x == 1) printf("** %d
", sign);
if (sign && !p2[i]) {
p2[i] = x;
sign = 0;
find(i);
p2[i] = 0;
sign = 1;
}
else if (!sign && !p1[i]){
p1[i] = x;
sign = 1;
p = i;
find(i);
p1[i] = 0;
sign = 0;
}
}
int t = 0;
for(int i = 2; i <= n; i++){
if (p2[i]) t++;
}
if (sign && t == n-1) {
sign = 0;
p2[1] = p;
if (check()) ans++;
p2[1] = 0;
}
}
int main() {
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
int t;
cin >> t;
while(t--){
scanf("%d%d", &n, &m);
memset(p2, 0, sizeof(p2));
memset(p1, 0, sizeof(p1));
ans = 0, sign = 1;
find(1);
printf("%lld
", ans);
}
return 0;
}