「程序設計思維與實踐」 CSP-M1:取模+模擬+dfs

A - 咕咕東的奇遇

題目描述

咕咕東是個貪玩的孩子,有一天,他從上古遺蹟中得到了一個神奇的圓環。這個圓環由字母表組成首尾相接的環,環上有一個指針,最初指向字母a。咕咕東每次可以順時針或者逆時針旋轉一格。例如,a順時針旋轉到z,逆時針旋轉到b。咕咕東手裏有一個字符串,但是他太笨了,所以他來請求你的幫助,問最少需要轉多少次。
在這裏插入圖片描述

input

輸入只有一行,是一個字符串。

Output

輸出最少要轉的次數。

Sample Input

zeus

Sample Output

18

題解

  • 考慮到從一個字符到下一個字符只有兩種方式,一種是逆時針,一種是順時針,於是思路是,比較一下兩種方式哪一種距離最近就用哪一種,最終狀態是一樣的。
  • 通過取模運算來獲取兩種方式的距離,給a…z編號爲0…25先比較一下當前位置和目標位置的編號大小,令小的爲minn,大的爲maxx,則兩個距離分別是minn+26-maxxmaxx-minn,將最小者加入到ans裏即可。

代碼

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <algorithm>
#include <cmath>
using namespace std;

int main() {
	string s;
	cin >> s;
	int len = s.length();
	int sum = 0, cur = 0;
	for(int i = 0; i < len; ++ i) {
		int x = s[i]-'a';
		int minn = min(x,cur), maxx = max(x,cur);
		int step = min(minn+26-maxx, maxx-minn);
		cur = x;
		sum += step;
		// cout << step << endl;
	}
	cout << sum << endl;
	return 0;
}

B - 咕咕東想吃飯

題目描述

咕咕東考試周開始了,考試周一共有n天。他不想考試周這麼累,於是打算每天都吃頓好的。他決定每天都吃生煎,咕咕東每天需要買ai​個生煎。
但是生煎店爲了刺激消費,只有兩種購買方式:
①在某一天一次性買兩個生煎。
②今天買一個生煎,同時爲明天買一個生煎,店家會給一個券,第二天用券來拿。
沒有其餘的購買方式,這兩種購買方式可以用無數次,但是咕咕東是個節儉的好孩子,他訓練結束就走了,不允許訓練結束時手裏有券。咕咕東非常有錢,你不需要擔心咕咕東沒錢,但是咕咕東太笨了,他想問你他能否在考試周每天都能恰好買ai​個生煎。

input

輸入兩行,第一行輸入一個正整數n(1<=n<=100000),表示考試周的天數。
第二行有n個數,第i個數ai​(0<=ai​<=10000)表示第i天咕咕東要買的生煎的數量。

Output

如果可以滿足咕咕東奇怪的要求,輸出"YES",如果不能滿足,輸出“NO”。(輸出不帶引號)這裏是引用

Sample Input1

4
1 2 1 2

Sample Output1

YES

Sample Input2

3
1 0 1

Sample Output2

NO

題解

  • 考慮到最終要求判斷的結果是最後一天有沒有券的剩餘,我們可以先假定最後一天滿足條件,那麼在這個前提下,前一天的購買狀態我們也一定能獲得,所以從最後一天往前推,知道推到第一天,讓第一天也滿足條件,最終檢查第0天(實際不存在的天)是否是0,即是否沒有溢出,即可獲取答案。
  • 嗷還有一件事,我們分析題目要求可以得知,在前一天和後一天同時消耗2次以上券的狀態,完全可以用分別購買一次2個生煎來代替,所以只需要考慮%2的結果即可。

代碼

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <algorithm>
#include <cmath>
using namespace std;

const int MAXN = 100010;
int days[MAXN];

int main() {
	int n;
	cin >> n;
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &days[i]);
	}
	days[0] = 0;
	for (int i = n; i >= 1; i--) {
		int x = days[i]%2; // if single digit
		if (x) { // if next day need my day
			if (days[i-1]) {
				days[i-1] --;
			} else {
				puts("NO");
				exit(0);
			}
		}
	}
	// cout << days[0] << endl;
	if(days[0] != 0) {
		puts("NO");
	} else {
		puts("YES");
	}

	return 0;
}

/*
4
1 2 2 2
*/

C - 可怕的宇宙射線

題目描述

衆所周知,瑞神已經達到了CS本科生的天花板,但殊不知天外有天,人外有苟。在浩瀚的宇宙中,存在着-種叫做苟狗的生物, 這種生物天生就能達到人類研究生的知識水平,並且天生擅長CSP,甚至有全國第一的水平!但最可怕的是,它可以發出宇宙射線!宇宙射線可以摧毀人的智商,進行降智打擊!
宇宙射線會在無限的二維平面上傳播(可以看做一個二維網格圖),初始方向默認向上。宇宙射線會在發射出一段距離後分裂,向該方向的左右45°方向分裂出兩條宇宙射線,同時威力不變!宇宙射線會分裂n次,每次分裂後會在分裂方向前進a個單位長度。
現在瑞神要帶着他的小弟們挑戰苟狗,但是瑞神不想讓自己的智商降到普通本科生zjm那麼菜的水平,所以瑞神來請求你幫他計算出共有多少個位置會被"降智打擊”。
在這裏插入圖片描述

input

輸入第一行包含一個正整數n,(n<=30)n,(n<=30) n,(n<=30)n,(n<=30),表示宇宙射線會分裂n次。
第二行包含n個正整數a1,a2,⋯,ana1,a2,⋯,an a_1,a_2,\cdots,a_na1​,a2​,⋯,an​,第ii ii個數ai(ai≤5)ai(ai≤5) a_i(a_i\le 5)ai​(ai​≤5)表示第ii ii次分裂的宇宙射線會在它原方向上繼續走多少個單位長度。

Output

輸出一個數ans,表示有多少個位置會被降智打擊。

Sample Input

4
4 2 2 3

Sample Output

39

題解

  • 首先分析可「降智」的範圍:如果只朝一個方向走的話,30*5也只有150,所以一個方向走的最遠距離便是150,爲了不讓我們的座標爲負,我們可以定義300*300的棋盤,從(150,150)開始dfs。
  • 對於dfs,樸素鐵定超時,但是我們可以考慮,在這麼小的棋盤範圍中,可能會有重複的狀態發生,即到了某個點,這時的分裂次數,方向都是相同的,那麼我們完全不用再來一遍,因爲結果肯定和之前是一樣的。所以我們把當前的狀態壓縮進一個數組中vis[300][300][MAXN][10];分別代表x,y座標,第幾次分裂,分裂方向。這樣每次進行標記和驗證就可以了。
  • 還要有一個記錄數組,記錄的是本點是否被走過。標記的同時加入到 ans 答案計數器裏最後輸出即可。
  • 注意這裏不能考慮的太細,比如標記在一個方向進行中的狀態,雖然時限會再減小,但是太難寫了寫不出來就像是考試的時候一樣TAT。
  • 還有一些小細節比如,左轉和右轉就可以用取模運算來解決。

代碼

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;

typedef long long LL;
const int MAXN = 35;
int dx[] = {0,1,1,1,0,-1,-1,-1};
int dy[] = {1,1,0,-1,-1,-1,0,1};
int n;
LL ans = 0;
int a[MAXN];
int vis[300][300][MAXN][10];
int ansvis[300][300];

void dfs(int x, int y, int cnt, int dir) {
	if(cnt > n) return;
	if(vis[x][y][cnt][dir]) return; // zhuangtai fangwenguo
	vis[x][y][cnt][dir] = true;
	for(int i = 1; i <= a[cnt]; ++i) {
		x += dx[dir];
		y += dy[dir];
		if(!ansvis[x][y]) {
			ansvis[x][y] = true;
			ans ++;
		}
	}
	dfs(x, y, cnt+1, (dir+1)%8); // right
	dfs(x, y, cnt+1, (dir+7)%8); // left
}

int main() {
	memset(vis, 0, sizeof(vis));
	memset(ansvis, 0, sizeof(ansvis));
	cin >> n;
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &a[i]);
	}
	dfs(150,150,1,0);
	cout << ans << endl;

	return 0;
}

/*
4
4 2 2 3

*/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章