男女比例问题

  1. **题目:
    第一行有一个正整数n,代表学校的人数。
    第二行有n个用空格隔开的数,这些数只能是0或1,其中,0代表一个女生,1代表一个男生。(n小于100000)
    输出格式 Output Format   输出一个非负整数。这个数表示在输入数据中最长的一段男女人数相等的子序列长度。如果不存在男女人数相等的子序列,请输出0。
    样例输入 Sample Input
    9
    0 1 0 0 0 1 1 0 0
    样例输出 Sample Output

    6

  2. 我的思路:

(1)在循环输入数据的同时,统计数字1出现的次数,记为count。

(2)当0,1个数相等时,答案就是n/2 。

(3)当数字1较少时(数字0少同理,此时count=n-count;count代表0出现的次数),遍历数据,创建一个新数组,数组中的元素代表数字1出现的位置,
比如location[6]=23;表示第6个1出现在数据中的第23位。

(4)定义函数,开始递归:在第(3)步中,记location[0]=left,location[count-1]=right,即标记1的始末位置。最开始搜索的区间是[0,count-1] 1>如果始末位置之间的距离即right-left+1(right,left的值是通过location数组得到的)比2*count要小或者等于2*count(2倍1的个数), 说明答案就是2*count,因为我们总能在区间[left,right]的外面补充0来满足0,1个数相等 2>如果始末位置大于n/2,说明答案要求的序列出现在[left,right]里面(因为外面没有数字1了),用类似1>的判断方法判断[1,count-1]和[0,count-2]是否满足题意, 满足便可以return得到答案 (用类似1>的方法的意思是,只要新的始末距离比新的2*count[因为2*count不满足,所以现在是2*(count-1)]小于或等于,即为所求答案, 此时[left,right]外面虽然有其他的1,但是这些1不起作用,因为前一次判断不通过,即0的个数比1多,所以外面的1不起作用。)

(5)因为这个递归调用时间是随着递归层次的增多呈指数增长,因此这里可以用记忆型递归的方法(用空间换时间),把每次递归调用的结果保存在数组里,
下次遇到相同的递归时,直接返回数组值。

(6)算法的时间复杂度应该是O( n 2 n^2 n2),因为用数组存储递归结果最坏可能存储 n 2 n^2 n2个。

  1. 参考代码:
#include<stdio.h>
#include<stdlib.h>
int max(int a, int b)
{
	return a > b ? a : b;
}
int* str;
int *location;
int sum1[20000];
int sum2[20000];
int findlongest(int left, int right, int count)	//count=min{boys,girls}
{
	if (sum1[left] != -1 && sum2[right] != -1)
	{
		return sum1[left];
	}
	if (location[right] - location[left] + 1 > 2 * count)
	{
		sum1[left] = max(findlongest(left + 1, right, count - 1), findlongest(left, right - 1, count - 1));
		sum2[right] = sum1[left];
		return sum1[left];
	}
	else {
		sum1[left] = 2 * count;
		sum2[right] = 2 * count;
		return 2 * count;
	}
}
int main()
{
	for (int i = 0; i < 20000; ++i)
	{
		sum1[i] = -1;
		sum2[i] = -1;
	}

	int n = 0;
	scanf_s("%d", &n);
	str = (int*)malloc(sizeof(int) * n);
	int less = 0;
	for (int i = 0; i < n; ++i)
	{
		scanf_s("%d", &str[i]);
		if (str[i] == 1)
			++less;
	}
	if (n == 1)
	{
		printf("0\n");
		return 0;
	}
	if (less > n / 2)
	{
		less = n - less;
		if (less == 0)
		{
			printf("0\n");
			return 0;
		}
		location = (int*)malloc(sizeof(int) * less);;
		int j = 0;
		for (int i = 0; i < n; ++i)
		{
			if (str[i] == 0)
				location[j++] = i;
		}
	}
	else
	{
		int j = 0;
		location = (int*)malloc(sizeof(int) * less);;
		for (int i = 0; i < n; ++i)
		{
			if (str[i] == 1)
				location[j++] = i;
		}
	}

	printf("%d", findlongest(0, less - 1, less));
	free(str);
	free(location);
	return 0;
}
  1. 借鉴同学的方法:
    (1)思路:
    在记录数据的同时把0记成-1,定义一个变量sum,每记录一个数据,sum更新一次,创建一个数组map,
    将sum以下标的形式(因为sum可能是负数,所以要对sum进行大于等于n的偏移,比如以sum+n为下标)
    存储在map中,当新的map[sum2]等于之前的map[sum1]时(map[sum+n]表示和为sum时,对应的数据的位置),
    说明在区间map[sum1]和map[sum2]之间有满足0,1个数相等的序列(减去n个1,又加了n个1,所以男女相等),此时比较大小后判断是否更新最大值。
    (2)同学的代码(部分):
  mapped[0 + n] = -1;
	 mapped[sum + n] = 0;
  for (int i = 1; i < n; i++) {
	sum = sum + value[i];
  if (mapped[sum + n] != null)
		ans = max(ans, i - mapped[sum + n]);
		else mapped[sum + n] = i;
}

算法复杂度应该是O(n)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值