1. 程式人生 > >『NOIP賽前模擬題題解』

『NOIP賽前模擬題題解』

NOIP賽前模擬題題解

T1. CZYZNOIP普及組模擬賽1-1.小X與位運算

題目描述

自從上次小X 搞定了完美數之後,他最近在研究一項和計算機密切相關的黑科技。要知道在計算機的內部,資料都是以二進位制的形式來進行儲存的,而它使用的計算方法也和我們平時的加減乘除四則運算 有所不同,它使用的是位運算。那什麼是位運算呢? 基礎位運算有三種符號,分別是 and,or,xor(分別對應 pascal 中的 and,or,xor 三種運算子 號)。

以 and 為例,兩個二進位制數在做 and 運算時,分別對兩個二進位制數的每一位做 and 運算。而對每一 位做and 運算時,遵守以下規則:只有當兩個數的這一位都是 1 時,運算結果才為 1,否則就是 0。例如 1101 和10101 做and 運算之後結果為101(高位不足用0 補齊,最後結果忽略前導0)。 通俗點講 and 運算就是按位做乘法,即將兩個二進位制數從高位到低位依次對齊,然後每一位上對齊 的兩個數相乘即得到這一位的結果 。我們可以列一個簡單的例子來說明個and運算:01101 and 10101 = 00101。

而or,xor 的運算方法類似,唯一不同的是在對每一位做運算時遵循的方法不同。 or 運算遵守以下規則:只有當兩個數的這一位都是 0 時,運算結果才為 0,否則就是 1。例如1101or10101=11101。

xor 運算遵守以下規則:只有當兩個數的這一位相同時,運算結果才為0,否則就是1。例如1101xor10101=11000 。

小 X想知道兩個很大很大的二進位制數,在做完位運算之後, 最後的結果是什麼。而小X 自己無法知道正確答案是什麼,他只好求助於你來幫助他解決這個問題。
輸入格式

輸入資料第一行是一個字串,由字元0 和1組成,表示一個二進位制數。 第二行也是一個字串,由字元0 和1組成,同樣表示一個二進位制數。 第三行還是一個字串,一定是and,or,xor三個中一種,表示運算子號。 注意輸入的二進位制數沒有前導零,字元個數可能會超過255 個。
輸出格式

輸出一行一個字串,由字元0和1 組成,表示最後運算得到的二進位制數。 注意輸出的二進位制數不能帶有前導零,即輸出的第一個字元不能為0。
樣例資料

input

110100
11001
or

output

111101

資料規模與約定

用n 來表示輸入的最長的二進位制數的長度。

對於30% 的資料,1 ≤ n ≤ 20。

對於70% 的資料,1 ≤ n ≤ 1000。

對於100% 的資料,1 ≤ n ≤ 100000。

時間限制:1s1s

空間限制:256MB256MB

解析

根據題意進行標記,暴力模擬位運算,輸出時注意去掉前導零即可。

#include<bits/stdc++.h>
using namespace std;
char num1[100080],num2[100080],op[10];
int Num1[100080]={},Num2[100080]={},OP,Maxlen,Ans[100080]={};
inline void input()
{
	scanf("%s",&num1);scanf("%s",&num2);
	scanf("%s",&op);
	int len1=strlen(num1),len2=strlen(num2);
	Maxlen=max(len1,len2);
	int t=Maxlen;
	for(int i=len1-1;i>=0;i--)Num1[t--]=num1[i]-'0';
	t=Maxlen;
	for(int i=len2-1;i>=0;i--)Num2[t--]=num2[i]-'0';
	if(op[0]=='a')OP=1;
	if(op[0]=='o')OP=2;
	if(op[0]=='x')OP=3;
}
inline void work()
{
	for(int i=1;i<=Maxlen;i++)
	{
		switch(OP)
		{
			case 1:Ans[i]=Num1[i]&&Num2[i];break;
			case 2:Ans[i]=Num1[i]||Num2[i];break;
			case 3:Ans[i]=Num1[i]==Num2[i]?0:1;break;
		}
	}	
	int flag=0;
	for(int i=1;i<=Maxlen;i++)
	{
		if(!flag&&!Ans[i])continue;
		if(Ans[i])flag=1;
		printf("%d",Ans[i]);
	}
	printf("\n");
}
int main()
{
	freopen("bignum.in","r",stdin);
	freopen("bignum.out","w",stdout);
	input();
	work();
	return 0;
}

更簡單的做法是直接用STL自帶的bitset二進位制數來完成本題要求的位運算。

#include<bits/stdc++.h>
#define INF 100000
using namespace std;
bitset<INF> s1,s2;
string ys;
int now;
bool b[INF];
int main()
{
	freopen("bignum.in","r",stdin);
	freopen("bignum.out","w",stdout);
	cin>>s1>>s2>>ys;
	if(ys=="or")	s1=s1|s2;
	if(ys=="xor")	s1=s1^s2;
	if(ys=="and")	s1=s1&s2;
	while(s1.any())	b[now]=s1[now],s1[now]=0,now++;
	while(now--)	cout<<b[now];
	return 0;
}

T2. CZYZNOIP普及組模擬賽1-2.小 X 與機器人

題目描述

小 X 最近對戰勝韓國圍棋大神李世石的 AlphaGo 很感興趣,所以小 X 自己寫了一個叫做 BetaGo 的人工智慧程式(簡稱 AI),這個 BetaGo 會做什麼呢? 小 X 首先想要讓 BetaGo 做到自己在棋盤上落子,這一點 AlphaGo 是由程式設計師來完成的。

小 X 的 設想是這樣的:在棋盤的邊框上放置一個小機器人,這個小機器人會沿著棋盤的邊框移動到最接近落子點的位置,然後伸出它的機械臂將棋子放到棋盤上。這裡面最關鍵的一步是如何讓小機器人在棋盤的邊框上沿著最短的路徑移動,小 X 想請你幫他編個程式解決這個問題。眾所周知,圍棋棋盤大小為 19 × 19(如下圖所示),圖中加粗的一圈即為邊框。我們用一對整數 (x, y) 來表示棋盤上第 x 條橫線(從下往上數)與第 y 條豎線(從左往右數)的交叉點,如上圖中邊框上的 A 點用(6,1)表示,B 點用(10,19)表示,小機器人初始時放置在 (x1, y1) 這個位置上,它想要移動到 (x2, y2) 這個位置上。

(x1, y1)和(x2, y2) 一定是棋盤邊框上的交叉 點 每一步小機器人可以從當前位置移動到相鄰(上下左右)的某個位置上,即每次可以從 (x, y) 移 動到 (x - 1, y)、(x + 1, y)、(x, y - 1)、(x, y + 1) 四個位置中的一個,但是它不能走出或走進棋盤, 也就是說它只能沿著棋盤的邊框移動到相鄰位置,這就意味著任一時刻相鄰位置都恰好只有兩個。 BetaGo 會告訴小機器人最少需要走多少步,但小 X 還是很擔心 BetaGo 有的時候會失控,從而告訴他一個錯誤值。為此小 X 只好求助你,希望你編一個程式計算從 (x1, y1) 沿著棋盤的邊框移動到 (x2, y2) 最少需要走多少步。上圖中從 A 點(6,1)移動到 B 點(10,19)最少需要走 32 步,移動路線是: (6,1)→(5,1)→(4,1)→(3,1)→(2,1)→(1,1)→(1,2)→(1,3)→…… →(1,19)→(2,19)→……→(10,19)。
輸入格式

輸入資料僅有一行包含四個用空格隔開的正整數表示 x1, y1, x2, y2。

資料保證 (x1, y1),(x2, y2) 一定是棋盤邊框上的交叉點。
輸出格式

輸出一行包含一個整數 ans,表示小機器人從 (x1, y1) 移動到 (x2, y2) 的最少步數。
樣例資料

input

6 1 10 19

output

32

資料規模與約定

對於 30% 的資料,(x1, y1),(x2, y2)在同一條邊框上。

對於另外 30% 的資料,(x1, y1),(x2, y2)在相鄰的兩條邊框上。

對於另外 40% 的資料,(x1, y1),(x2, y2)在相對的兩條邊框上。

時間限制:1s1s

空間限制:256MB

解析

根據題目的要求可知,起點和終點一點在邊框上,那麼就可以直接數學推導
算了,還是敲廣搜吧。

#include<bits/stdc++.h>
using namespace std;
struct position{int x,y;}Begin,End;
queue<position>Q;int vis[25][25]={},dis[25][25]={};
int dx[5]={0,0,0,1,-1},dy[5]={0,1,-1,0,0};
inline void input()
{
	scanf("%d%d%d%d",&Begin.x,&Begin.y,&End.x,&End.y);
}
inline bool Check(int x,int y)
{
	return (x==1&&y>0&&y<20)||(y==1&&x>0&&x<20)||(x==19&&y>0&&y<20)||(y==19&&x>0&&x<20);
}
inline void bfs()
{
	vis[Begin.x][Begin.y]=true;dis[Begin.x][Begin.y]=0;
	Q.push(position{Begin.x,Begin.y});
	while(!Q.empty())
	{
		position temp=Q.front();Q.pop();
		int x=temp.x,y=temp.y;
		for(int i=1;i<=4;i++)
		{
			int tx=x+dx[i],ty=y+dy[i];
			if(Check(tx,ty)&&!vis[tx][ty])
			{
				dis[tx][ty]=dis[x][y]+1;
				vis[tx][ty]=true;
				Q.push(position{tx,ty});
			}
		}
	}
	printf("%d\n",dis[End.x][End.y]);
}
int main()
{
	freopen("betago.in","r",stdin);
	freopen("betago.out","w",stdout);
	input();
	bfs();
	return 0;
}

那麼數學方法是這樣子的,假設起點,終點座標為 ( x 1 , x 2 ) (x_1,x_2) , ( y 1 , y 2 ) (y_1,y_2) ,我們分四種情況討論即可。
1. x 1 = x 2 , d i s m i n = y 1 y 2 2. y 1 = y 2 , d i s m i n = x 1 x 2 3. x 1 x 2 = 18 , d i s m i n = m i n ( 16 + y 1 + y 2 , 56 y 1 y 2 ) ( ) 4. y 1 y 2 = 18 , d i s m i n = m i n ( 16 + x 1 + x 2 , 56 x 1 x 2 ) ( ) 1.x_1=x_2,dis_{min}=|y_1-y_2| \\2.y_1=y_2,dis_{min}=|x_1-x_2| \\3.|x_1-x_2|=18,dis_{min}=min(16+y_1+y_2,56-y_1-y_2)(兩個方向) \\4.|y_1-y_2|=18,dis_{min}=min(16+x_1+x_2,56-x_1-x_2)(兩個方向)