FZU - 1981 Three kingdoms

题意:在三维坐标上有n个战士和m个弓箭手,所有弓箭手朝同一方向射箭,问可以射中多少弓箭手( 箭不会停止);


思路:坐标映射,因为箭的方向是一定的,所以每个战士和弓箭手都可以确定一条直线:

x = a[i].x +k*v.x;

y = a[i].y +k*v.y;

z = a[i].z + k*v.z;

//a[i]为战士的坐标,v为箭的方向

令 x = 0;则可以求出 y 和z,只有y和z都相等的情况下两点的方向和箭的方向一样;


#include <stdio.h>
#include <cstring>
#include <iostream>
#include <string>
#include <algorithm>
#define ll long long
using namespace std;
#define M 10010
struct Point
{
	ll x, y, z;
}a[M], b[M], v;

struct Node
{
	ll x, y, z;
	int id;
	friend bool operator == ( Node a, Node b )
	{
		return a.x == b.x && a.y == b.y;
	}
}nde[M*2];
int n, m, cnt;

bool cmp( Node a, Node b )
{
	if( a.x == b.x ){
		if( a.y == b.y ){
			if( a.z == b.z )    //相同的点
				return a.id > b.id;//所以把弓箭手排在前面
			return a.z < b.z;    //确定方向的
		}
		else return a.y < b.y;
	}
	return a.x < b.x;
}
void init()   //假如箭的x方向为0,则不能把直线映射到 x = 0这个面
{
	if( v.x == 0 ){
		if( v.y != 0 ){
			swap( v.x, v.y );
			for( int i = 0; i < n; i++ )
				swap( a[i].x, a[i].y );
			for( int i = 0; i < m; i++ )
				swap( b[i].x, b[i].y );
		}
		else{
			swap( v.x, v.z );
			for( int i = 0; i < n; i++ )
				swap( a[i].x, a[i].z );
			for( int i = 0; i < m; i++ )
				swap( b[i].x, b[i].z );
		}
	}
}
int main()
{
	int t = 0, T, ans, k;
	scanf( "%d", &T );
	while( T-- ){
		scanf( "%d%d", &n, &m );
		for( int i = 0; i < n; i++ )
			scanf( "%I64d%I64d%I64d", &a[i].x, &a[i].y, &a[i].z );
		for( int i = 0; i < m; i++ )
			scanf( "%I64d%I64d%I64d", &b[i].x, &b[i].y, &b[i].z );
		scanf( "%I64d%I64d%I64d", &v.x, &v.y, &v.z );
		init();
		ans = cnt = 0;
		for( int i = 0; i < n; i++ ){
			nde[cnt].x = a[i].y*v.x - a[i].x*v.y;
			nde[cnt].y = a[i].z*v.x - a[i].x*v.z;
			if( v.x > 0 )   //需要根据箭的方向来排序
				nde[cnt].z = a[i].x;
			else nde[cnt].z = -a[i].x;
			nde[cnt++].id = 0;
		}
		for( int i = 0; i < m; i++ ){
			nde[cnt].x = b[i].y*v.x - b[i].x*v.y;
			nde[cnt].y = b[i].z*v.x - b[i].x*v.z;
			if( v.x > 0 ) 
				nde[cnt].z = b[i].x;
			else nde[cnt].z = -b[i].x;
			nde[cnt++].id = 1;
		}
		sort( nde, nde+cnt, cmp );
		k = nde[0].id;
		// k = 1说明前面出现过弓箭手;
		for( int i = 1; i < cnt; i++ ){
			if( nde[i] == nde[i-1] ){
				if( k && nde[i].id == 0 ) ans++; //前面有弓箭手并且位置相同
				if( nde[i].id ) k = 1;
			}
			else k = nde[i].id; //位置不一样,重置k;
		}
		cout<<"Case "<<++t<<": ";
		cout<<ans<<endl;
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值