暴力求解技巧(气死你也想不到)

Ignatius and the Princess IV
来自 
HDU - 1029

题面:

给你n个数字,请你找出出现至少(n+1)/2次的数字。

输入

本题包含多组数据,请处理到EOF:
每组数据包含两行。 
第一行一个数字N(1<=N<=999999) ,保证N为奇数。 
第二行为N个用空格隔开的整数。

输出

对于每组数据,输出一行,表示要求找到的那个数

样例输入

5
1 3 2 3 3
11
1 1 1 1 1 5 5 5 5 5 5
7
1 1 1 1 1 1 1

样例输出

3
5
1

看到这里 ,相信大家都已经有了想法,暴力?高大上的解法?还是先PASS,有的时候(数据量不是特别大的时候),就可以先过一发暴力,如果不行,再换其他方法。我先附上暴力解法:

#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
int a[1000000];
int main()
{
	int i;
	int n;
	int num;
	while(cin>>n)
	{
		memset(a,0,sizeof(a));
		for(i=0;i<n;i++) 
		{
			cin>>num;
			a[num]++;
			if(a[num]==(n+1)/2)
			{
				cout<<num<<endl;
			}
		}	
	}
	return 0;
}

耗时998ms 快要超时了,对于我这个新手,这个暴力用的方法也是使我大吃一惊,用数组的下标来模拟这些数字,而数组中的值就是这个下标出现的次数。很妙。其实还有不用暴力的解法:


#include<stdio.h>
#include<algorithm>
using namespace std;
int a[1000000];
int main()
{
	int i,n;
	while(scanf("%d",&n)!=EOF)
	{
		for(i=0;i<n;i++)scanf("%d",&a[i]);
		sort(a,a+n);
		printf("%d\n",a[(n+1)/2-1]);
	}
	return 0;
}

先把数组的数据排序,然后 直接 去找那个下标(n+1)/2 ,在数组的位置需要 -1,就直接找出答案。

如果满足的数据在前面,那么(n+1)/2-1就是在满足数据的最后的那一位。如果满足的数据在最后的话:

满足数据在中间的话也是满足的:

看来是排序让他保证了不会错。耗时202ms ,是很好了。

2.美素数

HDU - 4548

题面:

小明对数的研究比较热爱,一谈到数,脑子里就涌现出好多数的问题,今天,小明想考考你对素数的认识。
  问题是这样的:一个十进制数,如果是素数,而且它的各位数字和也是素数,则称之为“美素数”,如29,本身是素数,而且2+9 = 11也是素数,所以它是美素数。
  给定一个区间,你能计算出这个区间内有多少个美素数吗?

Input

第一行输入一个正整数T,表示总共有T组数据(T <= 10000)。
接下来共T行,每行输入两个整数L,R(1<= L <= R <= 1000000),表示区间的左值和右值。

Output

对于每组数据,先输出Case数,然后输出区间内美素数的个数(包括端点值L,R)。
每组数据占一行,具体输出格式参见样例。

Sample Input

3
1 100
2 2
3 19

Sample Output

Case #1: 14
Case #2: 1
Case #3: 4

这个题也是,起初想到了 暴力遍历,判断,结局不用说,肯定超时,又想到素数打表,但是很奇怪,还是超时。

const int maxn=10000000;
bool vis[maxn];	 
int Prime[maxn]; 
int cnt;
void fun()
{
	cnt = 0;
	memset(vis,true,sizeof(vis));
	vis[0] = vis[1] = false;	
	vis[2] = true;
	for(int i = 2 ; i<=maxn ; i++)
	{
		if(vis[i])
		{
			Prime[cnt++] = i;
			for(int j = i+i ; j<=maxn; j+=i)
			{
				vis[j] = false;
			}

		}

	}
	

}

这个题是个双打表,解法很妙:

直接把每个数 是否是素数和 它每个位的和 是否是素数 ,全部打好表,来判断,如果只打了是否它本身是不是素数,然后在输入数据的时候,再现判断它每个位是否是素数 ,就会超时,造成没必要的麻烦。打完表后,这个ans[ ]数组中的数据就是2到这个 下标范围中所有既是素数,又是每位和 也是素数 的数量。在下面的输入数据时 ,就直接 ans[右范围]-ans[左范围-1],这里要减一个 1,数组第一个数据位置为0。 

#include<stdio.h>
#include<iostream>
#include<set>
#include<string.h>
using namespace std;
const int maxn=1000005;
typedef long long ll;
int ans[maxn];
bool vis[maxn];	 
int Prime[maxn]; 
int cnt;
void fun()
{
	cnt = 0;
	memset(vis,true,sizeof(vis));
	vis[0] = vis[1] = false;	
	vis[2] = true;
	for(int i = 2 ; i<=maxn; i++)
	{
		if(vis[i])
		{
			Prime[cnt++] = i;
			for(int j = i+i ; j<=maxn; j+=i)
			{
				vis[j] = false;
			}

		}
	}
}
int fun(int i)
{
	int sum=0;
	while(i)
			{
				sum=sum+i%10;
				i=i/10;
			}
	return sum;
	
}
int main()
{
	int sum=0; 
	fun();
	for(int i=2;i<=maxn;i++)
		{
			if(vis[i]&&vis[fun(i)])
				sum++;
			ans[i]=sum;
		}
	int t;
	cin>>t;
	int kase=0;
	while(t--)
	{
		
		int left,right;
		cin>>left>>right;
		int x=ans[right]-ans[left-1];
		printf("Case #%d: %d\n",++kase,x);
	} 	
	return 0;
}

耗时 62 ms.完美。

3.Jamie and Alarm Snooze  来自 codeforces 916A

题面

约翰喜欢睡觉。有一天,他必须以hh:mm起床。然而,他讨厌醒来,所以他想通过在NICE时间设置闹钟来唤醒不那痛痛。然后他会每隔x分钟按下贪睡按钮,直到hh:mm到达,然后才会醒来。他想知道按下贪睡按钮所需的最小次数是多少。
如果它包含数字'7',则被认为是NICE。例如,13:07和17:27是NICE,而00:48和21:34不是NICE。
请注意,警报和唤醒时间设置的时间不是同一天。 John可以设定一个NICE时间,这样他就可以醒来...... hh:mm。
形式上,找到尽可能小的非负整数y,使得hh:mm之前的时间x·y分钟的时间表示包含数字'7'。
约翰使用二十四小时制,所以在23:59到00:00之后。
输入
每个输入包含2行。
第一行是整数x(0 <x <61)。
下一行包含时间hh:mm,我们提供的形式是两位整数,hh和mm,我们提供的时间是00:00到23:59;
产量
输出他按下按钮的最小次数。

Example

3
11 23
2
5
01 07
0

代码实现:

#include<stdio.h>
#include<iostream>
#include<math.h>

using namespace std;

int main(){

	int x,h,m;

	while(cin>>x){

		int ans=0;

		cin>>h>>m;

		if(m%10==7||h%10==7) cout<<"0"<<endl;

		else{

			while(h%10!=7&&m%10!=7){

				m-=x;

				ans++;

				if(m<0){

					m+=60;

					h--;

					if(h<0) h+=24;

				}

			}

			cout<<ans<<endl;

		}

	}

	return 0;

}

这个题就是用暴力的解法,有个坑点,就是 时间 范围 是00:00 到 23:59 如果时间减到 00:00以后 ,就会 再来一天 ,从 24:00再开始减时间,这个 需要考虑,还有 这句话:形式上,找到尽可能小的非负整数y,使得hh:mm之前的时间x·y分钟的时间表示包含数字'7',时减过时间厚的时间点 要有数字 7,不是 在 x*y 时间 减去过程中出现 数字7.

上面代码 简洁清晰,希望大家可以学学。如果 题目 是暴力求解的 话,可以把代码实现变得简单点,将代码变得简单清晰也是一种技巧吧,不用多余的步骤,以免耗时超时。

看看我一开始过得代码:

#include<stdio.h>
#include<iostream>
using namespace std;
int main()
{
	int n;
	int h,m;
	int h1,m1;
	int k=0;
	int shuju;
	int flag=0,lock=0;
	int xianzaishijian;
	while(scanf("%d%d%d",&n,&h,&m)!=EOF)
	{		
		flag=0,lock=0;
		int zongshijian=h*60+m;
			for(k=0;;k++)
			{
				flag=0,lock=0;
				xianzaishijian=zongshijian-k*n;
				if(xianzaishijian<0)
					xianzaishijian+=24*60;
				
				h1=xianzaishijian/60;
				m1=xianzaishijian%60;
				while(h1)
				{
					shuju=h1%10;
					h1=h1/10;
					if(shuju==7)
					{
						printf("%d\n",k);
						flag=1;
						break;				
					}						
				}
				if(!flag)
				{
					while(m1)
					{
						shuju=m1%10;
						m1=m1/10;	
						if(shuju==7)
						{
							printf("%d\n",k);
							lock=1;
							break;	
						}	
					}
				}
			if(flag||lock)
				break;
			}
			
	}
	return 0;
}

不忍直视,代码 又乱 又冗长,又不好理解。

猜你喜欢

转载自blog.csdn.net/tsam123/article/details/85035768