对数器打表找规律

对数器是什么?

通常我们在笔试的时候或者参加编程大赛的时候,自己实现了一个算法,但是不能够判断该算法是否完全没问题,如果在比赛平台上验证,通常只会告诉你有没有错误,出了错不会告诉你哪里有问题,对于排错来说是非常坑爹的,所以对数器就横空出世了,对数器就是用一个绝对OK的方法和随机器生成的样本数据进行合体,如果你的算法是没问题的,那么和对数器的这个百分之百正确的方法一个元素一个元素的比较,也一定是equals的。如果返回false,说明你的算法有问题。

对数器的概念

1.有一个你想要测的方法a;
2.实现一个绝对正确但是复杂度不好的方法b;
3.实现一个随机样本产生器;
4.实现对比算法a和b的方法;
5.把方法a和方法b比对多次来验证方法a是否正确;
6.如果有一个样本使得比对出错,打印样本分析是哪个方法出错;
7.当样本数量很多时比对测试依然正确,可以确定方法a已经正确

对数器打表找规律的使用场景:

输入参数是简单类型,返回值也是简单类型

对数器打表找规律的过程:

1.可以用最暴力的实现求入参不大情况下的答案

2.打印入参不大情况下的答案,然后观察规律

3.把规律变成代码,就是最优解

对数器的例题

使用规格8和规格6的袋子买苹果问题

一家超市只有两种袋子,一种是最多装8个苹果的袋子和最多装6个苹果的袋子,如果你现在去买苹果,如果装不满袋子,就选择不买这些苹果,那么当买n个苹果,最少用几个袋子?

实例:

买7个苹果,一个6规格的袋子装不下,两个6规格的袋子装不满,那么就返回 -1

买8个苹果,一个8规格的袋子就可以装下,那么返回1

那么我们就可以用递归的方法建立一个对数器

如下:

# include <stdio.h>

int min(int a, int b)
{
	if (a > b)
		return b;
	else 
		return a;
}

int apple(int x)
{
	if (x < 0)
		return 999; //表示袋子装不满 
	else if (x == 0)
		return 0; // 表示刚刚好 
	int p1 = apple(x - 8); // 用8规格的袋子装
	int p2 = apple(x - 6); //用6规格的袋子装
	if ( p1 != 999 )
		p1 = p1 + 1; 
	if (p2 != 999)
		p2 = p2 + 1;
	return min(p1, p2);
} 

void mid(int y)
{
	int ans = apple(y);
	if (ans == 999)
		printf("%d : -1\n", y);
	else 
		printf("%d : %d\n", y, ans);
}


int main()
{
	for (int i=1; i<100; ++i)
		mid(i);
	return 0;
}

把苹果从1到100的情况全部打印出来

结果如下:

所以我们就可以直接写出一个O(1)的代码

# include <stdio.h>

int main()
{
	int apple;
	scanf("%d", &apple);
	if ((apple & 1) != 0)
		printf("-1\n");
	else if (apple < 18)
	{
		if (apple == 0)
			printf("0\n");
		else if (apple == 6 || apple == 8)
			printf("1\n");
		else if (apple == 12 || apple == 14 || apple == 16)
			printf("2\n");
		else 
			printf("-1\n");
	}
	else 
		printf("%d\n", (apple - 18)/8 + 3);
}

A和B轮流吃草最终谁会赢

 一共有n重量的草,两只牛轮流吃草,A牛先吃。

每只牛在自己的回合,吃草的重量必须是4的幂,1,4,16,64.....

谁在自己的回合正好把草吃完谁赢,根据输入的n,返回谁赢。

思路:

对于n重量的草,我们可以直接穷举两头牛所有吃草的情况,判断是否有可能A牛能赢。

代码:

# include <stdio.h>
int grass(int n, int a) // n表示剩余草的重量, a表示当前吃草的牛 
{
	if (n < 5) //特派 
	{
		if (n == 0 || n == 2)
			return 1 - a; //当剩余草为0或2时,都是对方赢,也就是1 - a 
		else 
			return a;
	}
	int pick = 1; //穷举所有的可能 
	while (pick <= n)
	{
		if (grass(n - pick, 1 - a) == a) //剩余的草,给敌人挑
                            如果最后的结果显示还是该选手赢,那么向上返回 
			return a;
		pick = pick * 4; //因为要尽可能要该选手赢,所以要遍历所有的结果。 
	}
	return 1-a; //遍历了所有的结果都不能该选手赢的可能,所以向上返回敌人赢的结果。 
}

int main()
{
	int x = 0;// 0表示A牛,1表示B牛 
	for (int i = 0; i<=100; ++i)
		printf("%d : %d\n", i, grass(i, x));
} 

结果为:

所以我们就可以直接写出一个O(1)的代码

# include <stdio.h>

int main()
{
	int n;
	scanf("%d", &n);
	if (n % 5 == 0 || n % 5 == 2)
		printf("B");
	else
		printf("A");
	return 0;
} 

判断一个数字是否是若干数量(数量>1)的连续正整数的和

实例:

12 = 3 + 4 + 5

返回true

代码:

# include <stdio.h>

int math(int n)
{
	for (int i=0; i<n; ++i)
	{
		int num = 0;
		for (int j=1+i; j<n; ++j)
		{
			if (num + j > n)
				break;
			else if (num + j == n)
				return 1;
			num = num + j;
		}
	}
	return 0;
}

int main()
{
	for (int i=1; i<100; ++i)
		printf("%d : %d\n", i, math(i));
} 

结果为:

所以我们就可以直接写出一个O(1)的代码

# include <stdio.h>
int main()
{
	int n;
	scanf("%d", &n);
	if (n == (n & (-n)))
		printf("0");
	else
		printf("1"); 
} 

求解好串个数

如果一个字符串里,它里面只含有一个长度>=2的回文子串,那么就称它为好串。

假设有一个长度为n的字符串,里面只有"r", "e", "d"这三个字符串,个数不限,求一共有多少种好串。

实例:

当长度为2时,一共有3种好串的可能

分别为: "rr", "ee", "dd"。

代码:

# include <stdio.h>
# include <malloc.h>
int made(int);
int is(char*, int, int);
int compare(char*, int);

int n;
char* path;

int made(int n) //构造一个n长度的path
{
	path = (char*)malloc(sizeof(char)*n);
	return compare(path, 0);
}

int is(char* path, int l, int r) //判断在[l, r]区间内,path数组是否存在回文子串
{
	while (l < r)
	{
		if (path[l] != path[r])
			return 0;
		l++;
		r--;
	}
	return 1;
} 

int compare(char* path, int k)
{
	if (k == n-1)
	{
		int cnt = 0;
		for (int i=0; i<n-1; ++i)
			for (int j=i+1; j<n; ++j)
			{
				if (is(path, i, j) == 1)
					cnt = cnt + 1;
				if (cnt >= 2) //回文子串个数
					break;
			}
		if (cnt == 1)
			return 1;
		else
			return 0;
	}
	else
	{
		int ans = 0; //递归穷举所有的可能
		path[k] = 'r';
		ans = ans + compare(path, k+1);
		path[k] = 'e';
		ans = ans + compare(path, k+1);
		path[k] = 'd';
		ans = ans + compare(path, k+1);
		return ans;
	}
}

int main()
{
	for (int i=1; i<100; ++i)
		printf("%d : %d\n", i, made(i));
} 

结果:

发现从4开始,长度为n的答案都是(n+1)*6

所以我们就可以直接写出一个O(1)的代码

# include <stdio.h>

int main()
{
	int n;
	if (n == 1)
		printf("0");
	else if (n == 2)
		printf("3");
	else if (n == 3)
		printf("18");
	else
		printf("%d", (n+1)*6)
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值