紫书_第八章_高效算法设计_8.3.3——巨人与鬼

巨人和鬼

一组n个巨人正与n个鬼进行战斗,每个巨人的武器是一个质子炮, 它可以把一串质子流射中鬼而把鬼消灭。质子流沿直线行进,在击中鬼时就终止。巨人决定采取下述策略。他们寻找鬼配对,以形成n个巨人─鬼对,。然后每个巨人同时向他选取的鬼射出一串质子流。我们知道,让质子流互相交叉是很危险的。因此巨人选择的配对方式应该使质子流都不会交叉。假定每个巨人和每个鬼的位置都是平面上的一个固定点,并且没有三个位置共线, 求一种配对方案。


解题思路:由题意知,其必存在一个解,这里利用分治的思想,利用一条线,把原区域分为两个区域,然后对这两个区域递归求解。


分割思路:

1.先找左下角节点

2.把其余点按其与左下角节点角度大小排序

3.从小到大遍历,当一边的巨人与鬼的数量相同时(这里利用正负1相加为0判断),储存答案,递归求解


分割图如下:

用p1 --p6分割

下面上代码(已加注释):

/*************************************************************************
	> File Name:my_jurenyugui.cpp
	> Author:chudongfang 
	> Mail:1149669942@qq.com 
	> Created Time: 2016年06月1日 星期三5 16时43分32秒
 ************************************************************************/

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
using namespace std;
typedef long long ll;

typedef struct node
{
	int id;
	int x;
	int y;
	int type;
	double angle; 
}NODE;
NODE p[1000];//结构体数组,用来储存每个人的信息
int ans[1000];//结构,ans[i]=j代表编号i和编号为j的相连
void sovel(int l,int r);

int main(int argc, char const *argv[])
{
	memset(ans,0,sizeof(ans));
	int x,y,t,len=1;
	while(scanf("%d%d%d",&t,&x,&y)!=EOF)// 录入数据 
	{
		p[len].x   =x;
		p[len].y   =y;
		p[len].id  =len;//对应ID
		if(t==1)
			p[len].type=1;//表示类型
		else
			p[len].type=-1;	
		len++;
	}		
	len--;

	sovel(1,len);
	
    for(int i=1;i<=len;i++)
		printf("%d ",ans[i]);
	return 0;
}

void sovel(int l,int r)//递归求解,分治思想
{
	NODE t;
	if(l>=r)  return ;
	int pos=l;//初始化为第一个
	/************步骤1**************/
    for(int i=l+1;i<=r;i++)//找编号l-r区域内左下角节点
		if(p[i].y<p[pos].y||p[i].y==p[pos].y&&p[i].x<p[pos].x)
			pos=i;

	t=p[l];//交换
	p[l]=p[pos];
	p[pos]=t;

	int cnt=p[l].type;//从第一个开始
    /**************步骤2*************/
	for(int i=l+1;i<=r;i++)//计算点与左下角点的角度大小
		p[i].angle=atan2(p[i].y-p[l].y, p[i].x-p[l].x);
	
    /*for(int i=l;i<=r;i++)
	{
		printf("%d %d %d %lf\n",p[i].id,p[i].x,p[i].y,p[i].angle);
	}*/
	
    for(int i=l+1;i<=r;i++)//按角度大小,把除左下角的元素排序
	{
		for(int j=i;j<=r;j++)
		{
			if(p[i].angle>p[j].angle)
			{
				t=p[i];
				p[i]=p[j];
				p[j]=t;
			}
		}
	}
	
    /*for(int i=l;i<=r;i++)
	{
		printf("%d %d %d %lf\n",p[i].id,p[i].x,p[i].y,p[i].angle);
	}*/
	/**************步骤3***************/
    for(int i=l+1;i<=r;i++)//排序后,从小到大遍历
	{
		cnt+=p[i].type;
		if(cnt==0)//当遍历过后的区域巨人和鬼的数量相同时
		{
			ans[p[l].id]=p[i].id;//链接左下角点和当前点
			ans[p[i].id]=p[l].id;
			sovel(l+1,i-1);//分治,递归求解左边区域
			sovel(i+1,r);//分治,递归求解右边区域
			break;
		}
	}
	return ;
}

注意这里采用EOF为录入结束标志。windows下用ctrl+z         linux下用ctrl+d

测试数据:

11 1

10 1

01 2

02 2


运行结果:

4 3 2 1







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值