大连海事大学/C语言程序设计实践小学期/N个狙击手问题/C语言

N个狙击手在进行生死搏斗,每位狙击手各瞄准了一个目标,一旦开枪就会将被瞄准的目标消灭,被消灭的狙击手无法再开枪。已知任意两个狙击手不会同时开枪,但不知道他们的开枪顺序,所以无法确定最后哪些狙击手会存活下来。

请你求出最少可能存活多少个狙击手,最多可能存活多少个狙击手。

输入描述:

第一行一个正整数N 接下来一行N个正整数,第i个数字Ti表示第i个狙击手瞄准了第Ti个狙击手,注意可能存在Ti=i

输出描述:

两行,第一行是最少存活的狙击手的数量,第二行是最多存活的狙击手的数量

#include<stdio.h>
#define bool char
#define ture 1
#define false 0
#define Maxxx 4595923
//这题存在两种极端情况
//除了X号外,其余所有人都指向X号
//没有任何一个人被两个人(包括两人)以上指为目标,构成了一个环
//非特殊情况下,由上述两种情况组成,构成环和线的组合
//一个人只能瞄准一个人
//按照题目所给例子答案,每个狙击手只能开一枪,否则最终结果无论如何只会剩1人
//被两个人以上瞄准,则早晚都会死,想要存活的人尽可能多,那么早晚会死的人不应该杀死人
//想要存活的人尽可能少,那么早晚会死的人应该杀死一人
int i,maxx,minn,N,j;
//不想用动态数组了,越用越麻烦,于此处设置一个极大的数作为数组长度
int select[Maxxx];//定义一个数组,用来储存每位狙击手瞄准的目标
//因为不可能存在第0位狙击手,因此从第二处开始储存数据
int aimed[Maxxx];//用来储存某位狙击手总被几人瞄准
int lucky[Maxxx];//储存没有被瞄准的幸运儿
bool d[Maxxx];//0生1死,若i号狙击手死亡,则有d[i]=1
bool ud[Maxxx];//可能死也可能没死
int person=1;
int will;

int main()
{
	printf("THE NUMBER OF SNIPERS IS:");
	printf("\n");
	scanf("%d",&N);
	for(i=1;i<=N;++i)//第i个数字Ti表示第i个狙击手瞄准了第Ti个狙击手,则i一定不可能为0
	{
		scanf("%d",&select[i]);//输入第i个狙击手瞄准的狙击手序号
		//输入值必须大于0小于等于狙击手总人数,但不知道如何做循化处理
		aimed[select[i]]++;//第select[i]号狙击手被瞄准次数+1
	}
	//如果有人没有被瞄准,那么这位狙击手一定会活下来
	//这位幸运的狙击手一定包括在最少、最多存活人数内
	for(i=1;i<=N;++i)
	{
		if(aimed[i]==0)
		{
			minn++;
			lucky[++maxx]=i;//幸运儿序号被储存
		}
	}
	while(person<=maxx)//如果没有人不被瞄准--完全的环,这个循环不会执行
	//循环的意义在于让幸运儿及时枪杀他们的瞄准目标,尽可能提高幸运儿的瞄准目标的瞄准目标的存活可能,使得最大存活人数尽可能增多
	//每次循环都会使得head增加1,让lucky数组中存储的下一位幸运儿登场
	//如果没有新的幸运儿诞生,maxx就不会增加
	//幸运儿的数量是有限的,但head可以无限增长,尽管数组lucky中不再有非零的元素,即幸运儿狙击手序号出现
	//总会有head>maxx,使得循环被打破

	{
		int now=lucky[person];
		//lucky(幸运儿数组)数组中的非零元都是幸运儿的序号
		//设幸运儿序号为i,则now被赋值为i
		person++;//这时person指向下一位狙击手代号
		if(d[select[now]])continue;//如果新的幸运儿与上一位幸运儿瞄准目标相同,那么他不必再开枪,continue被执行
		d[select[now]]=1;//d[i号幸运儿瞄准目标的序号]布尔值为1
		//代表i号幸运儿瞄准目标死亡
		will=select[select[now]];
		//will被赋为i号狙击手瞄准的某号狙击手的瞄准对象X号的序号
		//幸运儿一定存活,那么i号幸运儿的瞄准目标一定死亡
		//i号幸运儿的瞄准目标的瞄准目标存活可能性增大
		aimed[will]--;
		//以X号为瞄准目标的狙击手人数-1
		ud[will]=1;
		//表明X号为可能死也可能没死的狙击手
		if(aimed[will]==0)
		{
			lucky[++maxx]=will;//X号若只被一人瞄准,此时if判定一定满足,X号一定存活,maxx增加,新的幸运儿诞生
		}
	}
	for(i=1;i<=N;++i)
	{
		if(aimed[i] && !d[i])
		{
			int length=0,u=0;
			for(j=i;!d[j];j=select[j])
			{
				length++;
				u|=ud[j];//若j为可能死也可能没死的狙击手,u=1,若j已死,u=0
				d[j]=1;
			}
			//内层for循环其实是在幸运儿开完枪后,找出一条完整的狙击线,找不到时,length停止增加,u也不再改变
			//外层for循环是在找出所有狙击线
			//越先被找到的狙击手(发枪晚)越必死无疑
			if(!u && length>1)
			{
				minn++;//如果最后u为0,则代表找到了一条有始有终的狙击线,若length>1,代表狙击线人数大于1,minn++,多条狙击线累加得到minn
				maxx+=length/2;
			}
		}
	}
	printf("%d",N-maxx);
	printf("%d",N-minn);
	return 0;
}








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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值