理工 校赛 题
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\n", 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 \n", 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\n", ans); } return 0; }
东北日出西边雨 道是无情却有情