To_Heart—题解——数三角形

1.jpg
2.jpg

首先分析一下题目,知道我们的下标是无法成为单数的,所以要将(0,0)到(9,9)的空间看成(0,0)到(18,18)的,即将0.5个单位长度看成一个,所以得到以下代码:

int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
		int xx,xxx,yy,yyy;
		scanf("%d%d%d%d",&xx,&xxx,&yy,&yyy);
		xx+=xx;  //xx为x的横坐标,xxx为x的纵坐标,下面一样;
		yy+=yy;
		xxx+=xxx;
		yyy+=yyy;//将单位长度×2;
	}

然后我们需要定义一个a[i][j][k][l],其值为一表示如果i,j到k,l有火柴,否则相反。

我们知道,如果i,j到i+2,j+2有火柴棒,那么可以看为i,j到i+1,j+1,i+1,j+1到i+2,j+2都有一根火柴棒,那么我们可以在输入时就进行处理:

int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		int xx,xxx,yy,yyy;
		scanf("%d%d%d%d",&xx,&xxx,&yy,&yyy);
		xx+=xx;								//xx为x的横坐标,xxx为x的纵坐标,下面一样;
		yy+=yy;
		xxx+=xxx;
		yyy+=yyy;  //将单位长度×2;
		a[(xx+yy)/2][(xxx+yyy)/2][xx][xxx]=1;   //中点定理:(i,j)和(k,l)的中点为(i+k)/2,(j+l)/2;
		a[xx][xxx][(xx+yy)/2][(xxx+yyy)/2]=1;	//为了后文统计方便,我们可以把(i,j)到(k,l)的火柴同时看成(k,l)到(i,j)的火柴;
		a[(xx+yy)/2][(xxx+yyy)/2][yy][yyy]=1;
		a[yy][yyy][(xx+yy)/2][(xxx+yyy)/2]=1;
		a[xx][xxx][yy][yyy]=1;
		a[yy][yyy][xx][xxx]=1;
	}

那么我们又发现,如果(0,0)到(1,1)有火柴,且(1,1)到(2,2)有火柴,那么(0,0)到(2,2)也一定有火柴。

那么现在我们需要一个程序来判断共线。我们知道共线肯定会出现一根联通三点的直线,结合一下数学知识,不难发现共线其实就是一个一次函数,即y=ax+b(y为横坐标,x为纵坐标);

又结合数学知识,我们知道,一次函数可以是往上,即y随着x增大而增大,或是往下,即y随着x增大而减小;

那么我们只需要判断一下这两种情况就可以了:

bool PD(int x1,int y1,int x2,int y2,int x3,int y3){//判断坐标为(x1,y1),(x2,y2),(x3,y3)的三数是否共线;
	if(x1-y1==x2-y2&&x2-y2==x3-y3)			//y随着x变大而变大;				
		return 0;
	if(x1+y1==x2+y2&&x2+y2==x3+y3)			//y随着x变大而变小;				
		return 0;
	return 1;
}

但我们发现平行线也是一种共线的情况,所以加上平行线的判定方式:

bool PD(int x1,int y1,int x2,int y2,int x3,int y3){
	if((x1==x2&&x1==x3)||(y1==y2&&y2==y3))				
		return 0;
	if(x1-y1==x2-y2&&x2-y2==x3-y3)			//y随着x变大而变大;				
		return 0;
	if(x1+y1==x2+y2&&x2+y2==x3+y3)			//y随着x变大而变小;				
		return 0;
	return 1;
}

那么,我们已经处理完了所有情况,只需要暴力枚举三个数的坐标,并判断他们之间是否都有火柴,还要确保他们不共线(共线没有意义);

完整代码如下:

#include<bits/stdc++.h>
using namespace std;

int a[25][25][25][25]={};
bool f[25][25][25][25][25][25]={};

bool PD(int x1,int y1,int x2,int y2,int x3,int y3){
	if((x1==x2&&x1==x3)||(y1==y2&&y2==y3))				
		return 1;
	if(x1-y1==x2-y2&&x2-y2==x3-y3)			//y随着x变大而变大;				
		return 1;
	if(x1+y1==x2+y2&&x2+y2==x3+y3)			//y随着x变大而变小;				
		return 1;
	return 0;
}

int main(){
	int n,ans=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		int xx,xxx,yy,yyy;
		scanf("%d%d%d%d",&xx,&xxx,&yy,&yyy);
		xx+=xx;								//xx为x的横坐标,xxx为x的纵坐标,下面一样;
		yy+=yy;
		xxx+=xxx;
		yyy+=yyy;  //将单位长度×2;
		a[(xx+yy)/2][(xxx+yyy)/2][xx][xxx]=1;
		a[xx][xxx][(xx+yy)/2][(xxx+yyy)/2]=1;
		a[(xx+yy)/2][(xxx+yyy)/2][yy][yyy]=1;
		a[yy][yyy][(xx+yy)/2][(xxx+yyy)/2]=1;
		a[xx][xxx][yy][yyy]=1;
		a[yy][yyy][xx][xxx]=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 r=0;r<=18;r++) {
                        for(int z=0;z<=18;z++) {
                            if(a[i][j][k][l]==1&&a[k][l][r][z]==1&&!(i==k&&j==l)&&!(k==r&&l==z)) {  //暴力判断是否共线,如果有两个点相同,则没有判断的必要; 
                                if(PD(i,j,k,l,r,z)){
                                	a[i][j][r][z]=1;
                                	a[r][z][i][j]=1;
								}
                            }
                        }
                    }
                }
            }
        }
    }
	for(int i=0;i<=19;i++){
		for(int j=0;j<=19;j++){
			for(int k=0;k<=19;k++){
				for(int l=0;l<=19;l++){
					for(int r=0;r<=19;r++){
						for(int z=0;z<=19;z++){
							if(a[i][j][k][l]&&a[i][j][r][z]&&a[k][l][r][z]&&!f[i][j][k][l][r][z]&&!PD(i,j,k,l,r,z)){  //暴力判断三角形是否成立 
								f[i][j][k][l][r][z]=1;
								f[k][l][i][j][r][z]=1;		//如果(i,j)->(k,l)->(r,z)为三角形,那么(k,l)->(i,j)->(r,z)也是,则没有再计算的必要;
								f[r][z][i][j][k][l]=1;
								f[r][z][k][l][i][j]=1;
								f[i][j][r][z][k][l]=1;
								f[k][l][r][z][i][j]=1;		 //同理;
								ans++;
							}
						}
					}
				}
			}
		}
	}
	printf("%d\n",ans);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值