模擬算法,可以說是最基礎的算法了。
它的基本定義沒太多意思:就是去模擬題目的要求。題意要你怎麼做,你就怎麼做,看懂了題目,基本上就會做了。
舉一個大家耳熟能詳的栗子。
A+B Problem
給定兩個整數A和B,輸出他們的和。
這是學習C++的最簡單的例題之一。
代碼如下:
#include<iostream>
using namespace std;
int main()
{
int a,b;
cin>>a>>b;
cout<<a+b<<endl;
return 0;
}
那爲什麼要舉這麼簡單的栗子呢?
不知你有沒有注意,其實這就是一個模擬。
題目要你算A+B,你就算,這就是模擬。
我們在學習C++語言時做的題很多都有模擬的氣息,所以,這一個算法大家應該會比較熟悉了。
有些簡單的模擬題呢,很快就能做出來,當然個別難題還是要絞盡腦汁來想了。
下面,我們就來講解一下往年NOIP中,考到的一些模擬。
慄1.1.1-1 洛谷1003 鋪地毯
https://www.luogu.org/problemnew/show/P1003
題目描述
爲了準備一個獨特的頒獎典禮,組織者在會場的一片矩形區域(可看做是平面直角座標系的第一象限)鋪上一些矩形地毯。一共有 n 張地毯,編號從 1。到n。現在將這些地毯按照編號從小到大的順序平行於座標軸先後鋪設,後鋪的地毯覆蓋在前面已經鋪好的地毯之上。
地毯鋪設完成後,組織者想知道覆蓋地面某個點的最上面的那張地毯的編號。注意:在矩形地毯邊界和四個頂點上的點也算被地毯覆蓋。
輸入輸出格式
輸入格式:
輸入共n+2行
第一行,一個整數n,表示總共有n張地毯
接下來的n行中,第 i+1行表示編號i的地毯的信息,包含四個正整數
a,b,g,k ,每兩個整數之間用一個空格隔開,分別表示鋪設地毯的左下角的座標(a,b)以及地毯在x軸和y軸方向的長度第n+2行包含兩個正整數x和y,表示所求的地面的點的座標(x,y)
輸出格式:
輸出共1行,一個整數,表示所求的地毯的編號;若此處沒有被地毯覆蓋則輸出−1
輸入輸出樣例
輸入樣例#1:
3
1 0 2 3
0 2 3 3
2 1 3 3
2 2
輸出樣例#1:
3
輸入樣例#2:
3
1 0 2 3
0 2 3 3
2 1 3 3
4 5
輸出樣例#2:
-1
說明
【樣例解釋1】
如下圖,
1 號地毯用實線表示,
2 號地毯用虛線表示,
3 號用雙實線表示,覆蓋點(2,2)的最上面一張地毯是 3 號地毯。
【數據範圍】
對於30% 的數據,有
n≤2 ;
對於50% 的數據,
0≤a,b,g,k≤100;
對於100%的數據,有
0≤n≤10,000 ,
0≤a,b,g,k≤100,000。
noip2011提高組day1第1題
一般來說,Day1T1總是最簡單的。那麼,我們就來分析一下,這題該怎麼去模擬。
解題思路:
首先,我們看到了題目,第一眼就想到了用二維數組,枚舉每一個地毯,再給數組賦值便好!
來看一看代碼:
#include<iostream>
using namespace std;
int s[10001][10001],n;//二維數組用於存每一點的覆蓋情況
int x,y,a,b;//地毯的大小
int X,Y;//詢問座標
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>x>>y>>a>>b;
for(int j=x;j<=x+a;j++)//枚舉X軸
{
for(int t=y;t<=y+b;t++)//枚舉Y軸
{
s[j][t]=i;//二維數組賦值
}
}
}
cin>>X>>Y;
if(s[X][Y])
cout<<s[X][Y]<<endl;
else
cout<<"-1"<<endl;
return 0;
}
但是,注意:
0≤n≤10,000 ,
0≤a,b,g,k≤100,000。
數據範圍是阻止我們AC的重要敵人,你要是開一個像
s 100001
的數組,空間會爆啊啊啊!
那怎麼辦呢?我們來想一想辦法吧。
有一個真理:NOIP真題一定有標程,也就是答案,所以,我們珂以根據數據範圍來選擇算法或數據類型。
10^6啊,很大,100000*100000是絕對不可能的。
那我們想一想:
如果我們用數組來存x,y,a,b,在倒序枚舉每一塊地板,判斷(X,Y)是否在地毯所覆蓋的區間內。若沒有,則輸出-1
這個可以堪稱完美算法了。我們只需要開100000*4的數組就可以了,複雜度是O(n),完虐上一個算法QAQ
接下來我們來證明一下這個算法。
若一個點存在於一塊地毯內(不是最上面的),那麼他一定不存在與比它所在的地毯上層的地毯上,否則就跳出循環了。
這個算法證明很容易。記住,在考場上證明你的算法很重要。
那麼,我們來看代碼:
#include<iostream>
using namespace std;
int s[10001][5],n;//二維數組存x,y,a,b
int X,Y;//所求座標
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>s[i][1]>>s[i][2]>>s[i][3]>>s[i][4];
s[i][3]+=s[i][1];
s[i][4]+=s[i][2];
}
//s數組作用爲:s[i][1]:第i張地毯起始點的x軸;s[i][2]:第i張地毯起始點的y軸;s[i][3]:第i張地毯終點的x軸;s[i][4]:第i張地毯終點的x軸
cin>>X>>Y;
for(int i=n;i>=1;i--)
{
if(s[i][1]<=X&&s[i][2]<=Y&&s[i][3]>=X&&s[i][4]>=Y)
{
cout<<i<<endl;
return 0;
}
}
cout<<-1<<endl;//沒搜到輸出-1.
return 0;
}
這一題就算是完整地講完了。
大家可以好好品味,去洛谷上AC這題吧!
慄1.1.1-2
洛谷P1008 三連擊
https://www.luogu.org/problemnew/show/P1008
題目描述
將1,2,⋯,9共9個數分成3組,分別組成3個三位數,且使這3個三位數構成1:2:3的比例,試求出所有滿足條件的3個三位數。
輸入輸出格式
輸入格式:
木有輸入
輸出格式:
若干行,每行3個數字。按照每行第1個數字升序排列。
輸入輸出樣例
輸入樣例#1:
無
輸出樣例#1:
192 384 576
...
(輸出被和諧了)QAQ
這就是一道大模擬題,連輸入都沒有!
怪不得是普及組,來練練水
那麼,這題怎麼來做呢?
我們來思考一下:
讓我們依次枚舉三個三位數的個、十、百位,若遇見滿足a:b:c=1:2:3時就輸出,其實就行了。
我們來看看代碼吧:
#include<cstdio>
#include<cstring>
int i,j,a;
bool v[10];//a[i]表示第i個數已經用過了
int main()
{
for(i=192;i<=327;i++)//第一個數最小192,最大327。用數學計算的,可以減小複雜度。
{
memset(a,0,sizeof(a));
a=0;
v[i%10]=v[i/10%10]=v[i/100]=v[i*2%10]=v[i*2/10%10]=v[i*2/100]=v[i*3%10]=v[i*3/10%10]=v[i*3/100]=1;//統計數字
for(j=1;j<=9;j++)
a+=v[j];//a表示1-9這些數字是否全部齊了
if(a==9)
printf("%d %d %d\n",i,i*2,i*3);//如果齊了就輸出
/*
算法介紹:
這裏的i表示第一個數,上面那個很長的語句就是將3個三位數的個、十、百位統計,a來計算用了幾個數,若用了9個,輸出。
*/
}
return 0;
}
這就是這題的解法。題目本身沒有太大的難度,只是要求一個思維的縝密,詳解已經在代碼中了,我想這題的理解應該沒什麼問題。
照常,去AC一下吧!
好了,今天的課程就到這裏了,下一次我們將繼續通過例題講解熟悉模擬算法。我們下次見!
如果大家有問題或者想和我討論,可以加我的QQ:907916611,我很樂意爲您解答!