1. 程式人生 > >『NOIP普及組模擬考試題解』

『NOIP普及組模擬考試題解』

1. 水水の題

[問題描述] 嗯……水水的題。 ti。梯。電梯? emmmmm…… 那麼,有一座電梯,它上升一層需要 6 秒鐘,下降一層需要 4 秒鐘,如果 要讓它停下,它會停 5 秒鐘。 現在告訴你,它要按順序到達然後停靠的 N 個樓層,求它需要的時間。 電梯開始在 0 層,樓梯共有 100 層。 [輸入格式] 輸入包含 N+1 行。 第一行為 N,接下來的 N 行是它 N 個停靠的樓層。 [輸出格式] 輸出包含一行。 第一行一個整數, 為所需要的時間。。 [樣例輸入] 3 2 3 1 [樣例輸出] 41 [樣例說明] NULL [資料規模與約定] N<=50

解析

目標在當前位置的哪一個方向就往哪邊走,更新當前位置,累加答案,根據題意模擬即可,不要忘記判斷電梯原地停著的情況。

#include<bits/stdc++.h>
using namespace std;
inline void read(int &k)
{
	int x=0,w=0;char ch;
	while(!isdigit(ch))w|=ch=='-',ch=getchar();
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); 
	k=(w?-x:x);return;
}
int n,stop[100]={},now=0,ans=0;
inline void input()
{
	read(n);
	for
(int i=1;i<=n;i++)read(stop[i]); } inline void work() { for(int i=1;i<=n;i++) { if(stop[i]>now)ans+=(stop[i]-now)*6+5; else if(stop[i]<now)ans+=(now-stop[i])*4+5; else if(stop[i]==now)ans+=5; now=stop[i]; } } int main() { freopen("ti.in","r",stdin); freopen("ti.out","w",stdout
); input(); work(); printf("%d\n",ans); return 0; }

2. 水水の數

[問題描述] 一天,無聊的水水在紙上的不同位置寫著一個又一個數字。最終,水水一 共寫了 N 個數字,每個數字書寫位置的座標分別為(xi,yi)。 看著紙上的數字,水水突然腦洞大開。定義一個數字到另一個數字的“水 氏距離”為兩個數字在豎直方向上的差值。水水想在所有的數字中找到一個數 字,這個數字滿足其他所有數字到它的“水氏距離”之和最短。 然而,由於滿足條件的數字可能不止一個。因此,水水只想知道其他所有 數字到這個數字的“水氏距離”之和。 [輸入格式] 輸入包含 N+1 行。 第一行一個整數 N。 之後 N 行, 每行兩整數 xi,yi 表示第 i 個數字被寫在座標(xi,yi)處。 [輸出格式] 輸出包含 1 行。 第一行一個整數,即所求“水氏距離”之和。 [樣例輸入] 5 1 2 2 2 1 3 3 -2 3 3 [樣例輸出] 6 [樣例說明] 例如,點(2,2)滿足要求(不排除存在其他點也滿足要求),其他所有點到 達點(2,2)的距離之和為 0+1+4+1=6。 [資料規模與約定] 對於 40%的資料: 1<=N<=1000。 對於 60%的資料: 1<=N<=100000。 對於 100%的資料: 1<=N<=1000000, -100<=x,y<=100。

解析

x座標是沒用的,需要根據y座標排序。假設這個數字在y0y_0處,以上有P個數字,以下有Q個數字,若P<Q,則每向下移一個單位長度,距離之和就會減少Q-P個單位長度,反之同理,向上移動即可縮小距離之和,所以當P=Q時最優,這個數選在中位數處。 策略就是計算中位數,暴力統計累計答案。

#include<bits/stdc++.h>
using namespace std;
inline void read(int &k)
{
	int x=0,w=0;char ch;
	while(!isdigit(ch))w|=ch=='-',ch=getchar();
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); 
	k=(w?-x:x);return;
}
struct pos{int x,y;}Pos[1000080]={};int n,mid,ans=0;
inline bool cmp(pos p1,pos p2){return p1.y<p2.y;}
inline void input()
{
	read(n);
	for(int i=1;i<=n;i++)read(Pos[i].x),read(Pos[i].y);
	sort(Pos+1,Pos+n+1,cmp);
	if(n%2==0)mid=n/2;
	else mid=n/2+1;
}
inline void Find_Mindis()
{
	for(int i=1;i<=n;i++){ans+=abs(Pos[i].y-Pos[mid].y);}
}
int main()
{
	freopen("num.in","r",stdin);
	freopen("num.out","w",stdout);
	input();
	Find_Mindis();
	printf("%d\n",ans);
	return 0;
}

3. 水水の字

[問題描述] 水水是一名出色的 OIer,在訓練過程中,他發現各大 OJ 並不是很好用, 於是決定自己建立一個 OJ。 可是水水希望在建立 OJ 系統之前瞭解他一共要創 建多少個資料夾。 在即將構建 OJ 的 Linux 系統下可以用路徑來描述資料夾,路徑為一個包 含若干部分的字串,之間用“/”(不含引號)分隔。每部分均為一個資料夾 的名稱,且表示這個資料夾的父資料夾為前一部分描述的資料夾。每個路徑的 第 1 個字元總是“/”,且不會出現兩個連續的“/”,最後一個字元不會是“/”。 而所有資料夾僅包含數字和字母。 例如: /home/fj/summer 表示根目錄下有一個名稱為 home 的資料夾, 這個 home 資料夾下有一個名稱 fj 的資料夾,這個名稱為 fj 的資料夾下有一 個名稱為 summer 的資料夾。此時要構造出這樣的路徑關係,除根目錄外還需 要 3 個資料夾。 現在先給出 N 個路徑,一開始除了根目錄不存在任何資料夾,在每給出一 個路徑後,對於第 i 個路徑,你需要輸出的是若要讓第 1 個路徑到第 i 個路徑 存在,最少需要新建多少個資料夾。 [輸入格式] 輸入包含 N+1 行。 輸入檔案第 1 行為一個正整數 N。 接下來 N 行,每行為一個描述路徑的字串,長度均不超過 100。 [輸出格式] 輸出包含 N 行。 每行 1 個正整數,第 i 行輸出若要使第 1 個路徑到第 i 個路徑存在,最少 需要新建多少個資料夾。 [樣例輸入] 6 /data /data/build /data/eat /data/build /build/build /data [樣例輸出] 1 2 3 3 5 5 [樣例說明] NULL [資料規模與約定] 對於所有資料, N<=1000。 對於部分資料,有 N<=20; 對於部分資料,有 N<=200; 對於部分資料,有對於所有路徑最多存在兩個“ /”(包含第 1 個字元)。

解析

這個問題有hash,map,trie樹三種做法。我當然是用最簡單最偷懶的map啦。 以“/”符號為間隔,利用字串加法提取並用map記錄每一個字串的字首子串,如果之前已經記錄過,那就不用累加,如果沒有記錄過,累加答案並記錄即可。

#include<bits/stdc++.h>
using namespace std;
inline void read(int &k)
{
	int x=0,w=0;char ch;
	while(!isdigit(ch))w|=ch=='-',ch=getchar();
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); 
	k=(w?-x:x);return;
}
int n,ans=0;map<string,bool>Check;
char s[1080][1080];
inline void input()
{
	read(n);
	for(int i=1;i<=n;i++)scanf("%s",&s[i]);
}
inline void work()
{
	for(int i=1;i<=n;i++)
	{
		string temp;int len=strlen(s[i]);
		for(int j=0;j<len;j++)
		{
			temp+=s[i][j];
			if(s[i][j+1]=='/'||j==len-1)
			{
				if(!Check.count(temp))
				{
					Check[temp]=1;
					ans++;
				}
			}
	    }
	    printf("%d\n",ans);
	}
}
int main()
{
	freopen("dir.in","r",stdin);
	freopen("dir.out","w",stdout);
	input();
	work();
	return 0;
}

4. 水水の圖

[問題描述] 水水將去 K 國旅遊。 K 國有 N 個省份,其中, 1 號省份是首都。 N 個省份之間有 M 趟火車,火車是雙向的。定義第 i 個省份的轉車次數為 從首都到這個省份,最少需要轉幾趟火車。如果從首都無法到達 i 省份,那麼 i 省份的轉車次數是無窮大。 由於洪水等自然原因,有些火車會停運。 當一趟火車停運之後, 2~N 每個省份的轉車次數不變,這種情況水水尚且 可接受,否則,水水則可能取消這次旅行。 現在水水想提前知道,哪些火車的停運是可接受的?以便提早做出旅行決 定。 如果可接受的火車不存在,輸出一行“NO”(不含引號)。 [輸入格式] 輸入包含 M+1 行。 第一行兩個正整數 N, M。 接下來 M 行每行兩個正整數 a, b。 按 1~M 的順序表示第 i 趟火車連線 a 和 b 兩個省份。 保證 a 不等於 b,且同一對 a, b 只會出現一次。 [輸出格式] 若干整數,從小到大排序,表示所有的可接受取消的火車序號。 [樣例輸入] 5 6 1 2 1 3 1 4 3 4 2 5 3 5 [樣例輸出] 4 5 6 [樣例說明] 如果第 1 趟火車被取消, 2 號省份到首都原本需要 0 次轉車(有直達火 車),現在需要 2->5->3->1,轉車 2 次, 不可接受。 如果第 2 趟火車被取消, 3 號省份到首都原本需要 0 次轉車(有直達火 車),現在需要 3->4->1,轉車 1 次, 不可接受。 如果第 3 趟火車被取消, 4 號省份到首都原本需要 0 次轉車(有直達火 車),現在需要 4->3->1,轉車 1 次, 不可接受。 [資料規模與約定] 對於 40%的資料: N, M<=500 對於 70%的資料: N<=500 M <= 50000 對於 100 的資料%: N, M<=200000 保證初始給定圖中所有點的轉機次數不是無窮大。

解析

圖的模型。 考慮一條邊可以去掉必須滿足兩種情況: 1.有一條從起點到該點路徑長度相同的邊從另一個節點連線過來,相當於有一條距離等價的的路徑 2.這一條邊不在最短路的樹上,也就是說這條邊無法構成最短路 由於邊的權值都是1,那麼用bfs跑出最短路的樹並記錄等價路徑,在一重迴圈列舉每一條邊,根據1.2.兩個條件判斷是否可以去除即可。

#include<bits/stdc++.h>
using namespace std;
inline void read(int &k)
{
	int x=0,w=0;char ch;
	while(!isdigit(ch))w|=ch=='-',ch=getchar();
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); 
	k=(w?-x:x);return;
}
struct Edge{int y,next;}edge[800080]={};
int t=0,n,m,dis[800080]={},Link[800080]={},e[800080][2]={},vis[800080]={},in[800080]={};
inline void add(int x,int y){edge[++t].y=y;edge[t].next=Link[x];Link[x]=t;}
inline void input()
{
	read(n),read(m);
	for(int i=1;i<=m;i++)
	{
		int u,v;
		read(u),read(v);
		e[i][0]=u,e[i][1]=v;
		add(u,v),add(v,u);
	}
}
inline void Search()
{
	memset(dis,0x3f,sizeof(dis));
	memset(vis,0x00,sizeof(vis));
	queue< int >Q;Q.push(1);
	dis[1]=0,vis[1]=1;
	while(!Q.empty())
	{
		int temp=Q.front();Q.pop();
		for(int i=Link[temp];i;i=edge[i].next)
		{
			if(!vis[edge[i].y])
			{
				vis[edge[i].y]=true;
				dis[edge[i].y]=dis[temp]+1;
				Q.push(edge[i].y);
				in[edge[i].y]=1;
			}
			else if(dis[edge[i].y]==dis[temp]+1)in[edge[i].y]++;
		}
	}
}
inline void Check()
{
	bool flag=0;
	for(int i=1;i<=m;i++)
	{
		int u=e[i][0],v=e[i][1];
		if(dis[u]>dis[v])swap(u,v);
		if(dis[v]-dis[u]!=1||in[v]>1)
		{
			printf("%d\n",i);
			flag=1;
		}
	}
	if(!flag)printf("NO\n");
}
int main()
{
	freopen("train.in","r",stdin);
	freopen("train.out","w",stdout);
	input();
	Search();
	Check();
	return 0;
}