1. 程式人生 > >暴力求解技巧(氣死你也想不到)

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

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;
}

不忍直視,程式碼 又亂 又冗長,又不好理解。