数三角题解【noip难题】

1.题目:

这是一个数三角的游戏。长度为1或SQRT(2)的小木棍放在一个网格上。如图所示,有水平的,垂直的或对角的。对角放置的木棍可以交叉。
在这里插入图片描述
将木棍随意地放在网格上得到的图案可能不含三角形,也可能含一个或多个三角形。如下图所示,
在这里插入图片描述
(a),(b),©,(d)和(e)分别含有2,5,12,0,0个三角形。你的任务是写一个程序数出一个图案中的三角形个数。。cpp

输入文件count.in包括N+1行:
先输入图案中木棍的个数N。下面输入这N根木棍的位置,用两个网格坐标表示,这两个坐标分别为木棍两端的位置。网格大小不超过10´10,因此网格左下和右上的坐标分别为(0,0)和(9,9)。

输入文件count.out包括1行:
三角形的个数。

样例:

输入:

3
0 0 0 1
0 0 1 0
0 1 1 0

输出:

1

题解:

一 :整理数据:

设a[i][j][k][l]表示(i, j)与(k, l)有联通;
首先输入x1, _y, x2, y2分别表示两点坐标,之后再将每个点×2,使得之后判断对角线情况更加容易(要不然会出现下标为小数情况)。

1.在同一直线,即是曼哈顿距离为2:

在这里插入图片描述

1):此时若两点为水平的(x1 == x2),那么A,C联通, C,B联通,同时将A,B连通(因为可能B在A前面所以要判断一下,但方法和此时相同),注意为了防止之后的联通路有问题,所以还要将点反过来在标记一遍如b[A][B]=b[B][A] = 1

	if(x1 + _y - x2 - y2 == 2) { 
		if(x1  == x2) {//如果两点在同一水平线上
			a[x1][_y + 1][x1][y2] = 1;//B,C
			a[x1][y2][x1][_y + 1] = 1;//反过来
			a[x1][_y + 1][x1][_y] = 1;//A,C;
			a[x1][_y][x1][_y + 1] = 1;//反过来
		}
	}//这里只写了B在A前的情况,另一种读者自行思考

在这里插入图片描述

2):若两点为竖直状态(y1 == y2)同样A,C联通, C,B联通,同时将A,B连通,还有也要判断B在A上面的情况,以及反复标记。

	if(x1 + _y - x2 - y2 == 2) {
		//如果两点在同一水平线上 
		if(_y == y2) {
			//若竖着的一条线 
			a[x1 + 1][_y][x2][_y] = 1;
			a[x2][_y][x1 + 1][_y] = 1;//两点分别连一次 
			a[x1 + 1][_y][x1][_y] = 1;
			a[x1][_y][x1 + 1][_y] = 1;
		}//这里只写了B在A上的情况,另一种读者自行思考
	}

2.在两对角线上,即是曼哈顿距离为4:

在这里插入图片描述

1):如图如果是这种状态,那么同理连接A,C与B,C和A, B,注意C可表示为(bx + 1, by + 1),同样的这里也有A在前或B在前的情况。

if(x1 + _y - x2 - y2 == 4) {//B在前
	a[x1][_y][x2 + 1][y2 + 1] = 1;//连接A,C
	a[x2 + 1][y2 + 1][x1][_y] = 1;//反向连接
	a[x2][y2][x2 + 1][y2 + 1] = 1;//连接B,C
	a[x2 + 1][y2 + 1][x2][y2] = 1;
}
if(x2 + y2 - x1 - _y == 4) {//A在前
	a[x1 + 1][_y + 1][x2][y2] = 1;//B,C
	a[x2][y2][x1 + 1][_y + 1] = 1;
	a[x1][_y][x1 + 1][_y + 1] = 1;//A,C
	a[x1 + 1][_y + 1][x1][_y] = 1;
}

在这里插入图片描述
当然也会有这种状态,前面的可以和之前方法一样,也可以改成一个if,首先这种方法有一个特点——xa + ya == xb + yb,不止如此,C点可以表示为((xa + xb)/ 2, (ya + yb)/ 2),原因很简单,因为这是正方形,而c又是对角线交点,所以即有上面公式(实在不会去看看一次函数)。

3.连接两点:

a[x1][_y][x2][y2] = 1;
a[x2][y2][x1][_y] = 1;

二.“弗洛伊德”联通:

用六重循环枚举中转点坐标E,A坐标,C坐标。然后再判断他们中间存不存在通路,紧接着在判断是否共线,如果是则连接。详细见代码:

	for(int i = 0;i <= 18; i++) {
		for(int j = 0;j <= 18; j++) {
			for(int k = 0;k <= 18; k++) {
				for(int l = 0;l <= 18; l++) {
					for(int m = 0;m <= 18; m++) {
						for(int n = 0;n <= 18; n++) {
							if(a[i][l][j][m] == 1 && a[i][l][k][n] == 1 && !(i == j && l == m) && !(i == k && l == n)) {
							//判断两个点是不是同一个点以及是否为通路
								if((n - l) * (j - i) - (m - l) * (k - i) == 0) {//如果两点之间有通路 && 两点为一条直线即可连接 
									a[j][m][k][n] = 1;
									a[k][n][j][m] = 1;
								}
							}
						}
					}
				}
			}		
		}
	}

注意:

“(n - l) * (j - i) - (m - l) * (k - i) == 0”为什么满足这样的条件即是共线呢?原因很简单,因为如果共线那么Kae = Keb即(ay - ey)/(ax - ex) == (ey - by)/(ex - bx),然后交叉相乘化简即可。

输出:

输出不多说,和联通差不多只是枚举三角形三点坐标,然后判断是否可连通,之间不要忘了要判断是否有两个相同的点,最后三点不可共线。

	for(int i = 0;i <= 18; i++) {
		for(int j = 0;j <= 18; j++) {
			for(int k = 0;k <= 18; k++) {
				for(int l = 0;l <= 18; l++) {
					for(int m = 0;m <= 18; m++) {
						for(int n = 0;n <= 18; n++) {
							if(a[i][l][j][m] == 1 && a[i][l][k][n] == 1 && a[j][m][k][n] == 1 && !(i == j && l == m) && !(j == k && m == n) && !(i == k && l == n)) {
							//三点可连通?是否为同点
								if((n - l) * (j - i) - (m - l) * (k - i) != 0) {
								//判断共线不共线
									ans++;//计数器++
								}
							}
						}
					}
				}
			}		
		}
	}

最后ans要除六,原因是:假设三点为A,B,C;
那么有(A,B,C)(A,C,B)(B,A,C)(C,B,A)(C,A,B)(B,C,A)共六种重复方案。

代码:

#include <iostream>
#include <cstdio>
using namespace std;
int t, a[35][35][35][35], x1, _y, x2, y2;
//a[i][j][k][l]表示(i, j)与(k, l)有联系; 
int main() {
	scanf("%d", &t);
	for(int i = 1;i <= t; i++) {
		scanf("%d %d %d %d", &x1, &_y, &x2, &y2);
		x1 *= 2;_y *= 2;x2 *= 2;y2 *= 2;
		if(x1 + _y - x2 - y2 == 2) {
			//如果两点在同一水平线上 
			if(_y == y2) {
				//若竖着的一条线 
				a[x1 + 1][_y][x2][_y] = 1;
				a[x2][_y][x1 + 1][_y] = 1;//两点分别连一次 
				a[x1 + 1][_y][x1][_y] = 1;
				a[x1][_y][x1 + 1][_y] = 1;
			}
			if(x1  == x2) {
				a[x1][_y + 1][x1][y2] = 1;
				a[x1][y2][x1][_y + 1] = 1;
				a[x1][_y + 1][x1][_y] = 1;
				a[x1][_y][x1][_y + 1] = 1;
			}
		}
		if(x1 + _y - x2 - y2 == 2){
			if(_y == y2) {
				a[x1][_y][x2 + 1][_y] = 1;
				a[x2 + 1][_y][x1][_y] = 1;
				a[x2][_y][x2 + 1][_y] = 1;
				a[x2 + 1][_y][x2][_y] = 1;
			}
			if(x1  == x2) {
				a[x1][_y][x1][y2 + 1] = 1;
				a[x1][y2 + 1][x1][_y] = 1;
				a[x1][y2 + 1][x1][y2] = 1;
				a[x1][y2][x1][y2 + 1] = 1;
			}
		}
		if(x1 + _y - x2 - y2 == 4) {
			a[x1][_y][x2 + 1][y2 + 1] = 1;
			a[x2 + 1][y2 + 1][x1][_y] = 1;
			a[x2][y2][x2 + 1][y2 + 1] = 1;
			a[x2 + 1][y2 + 1][x2][y2] = 1;
		}
		if(x2 + y2 - x1 - _y == 4) {
			a[x1 + 1][_y + 1][x2][y2] = 1;
			a[x2][y2][x1 + 1][_y + 1] = 1;
			a[x1][_y][x1 + 1][_y + 1] = 1;
			a[x1 + 1][_y + 1][x1][_y] = 1;
		}
		if(x1 + _y == x2 + y2) {//连接对角线 
			a[x1][_y][(x2 + x1) / 2][(_y + y2) / 2] = 1;
			a[(x2 + x1) / 2][(_y + y2) / 2][x1][_y] = 1;
			a[x2][y2][(x2 + x1) / 2][(_y + y2) / 2] = 1;
			a[(x2 + x1) / 2][(_y + y2) / 2][x2][y2] = 1;
		}
		a[x1][_y][x2][y2] = 1;
		a[x2][y2][x1][_y] = 1;
	}
	for(int i = 0;i <= 18; i++) {
		for(int j = 0;j <= 18; j++) {
			for(int k = 0;k <= 18; k++) {
				for(int l = 0;l <= 18; l++) {
					for(int m = 0;m <= 18; m++) {
						for(int n = 0;n <= 18; n++) {
							if(a[i][l][j][m] == 1 && a[i][l][k][n] == 1 && !(i == j && l == m) && !(i == k && l == n)) {//判断两个点是不是同一个点 
								if((n - l) * (j - i) - (m - l) * (k - i) == 0) {//如果两点之间有通路 && 两点为一条直线即可连接 
									a[j][m][k][n] = 1;
									a[k][n][j][m] = 1;
								}
							}
						}
					}
				}
			}		
		}
	}
	int ans = 0;
	for(int i = 0;i <= 18; i++) {
		for(int j = 0;j <= 18; j++) {
			for(int k = 0;k <= 18; k++) {
				for(int l = 0;l <= 18; l++) {
					for(int m = 0;m <= 18; m++) {
						for(int n = 0;n <= 18; n++) {
							if(a[i][l][j][m] == 1 && a[i][l][k][n] == 1 && a[j][m][k][n] == 1 && !(i == j && l == m) && !(j == k && m == n) && !(i == k && l == n)) {//判断两个点是不是同一个点 
								if((n - l) * (j - i) - (m - l) * (k - i) != 0) {
									ans++;
								}
							}
						}
					}
				}
			}		
		}
	}
	printf("%d", ans / 6);
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/cqbz_lipengcheng/article/details/107584774