1. 程式人生 > 其它 >1055 集體照 (25 point(s))

1055 集體照 (25 point(s))

// 未精簡全AC程式碼
// Bob175 Tom 188 Joe 190 Nick186
// Ann168 Mike170 Eva 168
// Tim160 Amy 160 John159

#include <bits/stdc++.h>
using namespace std;

struct Stu{
	string name;
	int height;
	bool operator < (Stu a) const{
		// 不相等按身高非遞減序 
		if(height != a.height)
			return height > a.height;
		// 否則返回名字升序 
		return name < a.name;
	}
};

void print(vector<Stu>& output){
	int first = 0;
	for(auto o : output)
		cout << (first++ ? " " : "") << o.name;
	cout << endl;
	// 輸出完清空元素輸出下一排
	output.clear(); 
}

int main() {
	// 讀取總人數N人總排數K排
	int n, k;
	set<Stu> stu;
	vector<Stu> output; 
	cin >> n >> k;
	
	// 讀取名字 身高
	for(int i = 0; i < n; i++) {
		string name; int height;
		cin >> name >> height;
		stu.insert({name, height}); 
	}
	
	// 算出最後一排多出來的人數
	int last = n - n / k * k, flag = 1;
	
	// 第一排要塞多出來的人數
	auto s = begin(stu);
	for(int i = 0; i < n / k + last; i++, s++){
		// 中間到兩旁非增序 從中間先左後右交替入隊(反過來 
		// 第一個直接推入 併到處理下一個 
		if(i == 0) {
			output.push_back(*s);
			continue;
		}
		// flag = 1 插入左邊 flag = 0 插入右邊
		if(flag == 1){
			flag = 0;
			output.insert(begin(output), *s);
		} 
		else{
			flag = 1;
			output.push_back(*s);
		}
	} 
	// 排完一隊直接輸出
	print(output); 
	
	// 輸出剩餘排 k - 1  重置flag 
	for(int j = 0; j < k - 1; j++){
		flag = 1;
		for(int i = 0; i < n / k; i++, s++){
			// 中間到兩旁非增序 從中間先左後右交替入隊(反過來 
			// 第一個直接推入 併到處理下一個 
			if(i == 0) {
				output.push_back(*s);
				continue;
			}
			// flag = 1 插入左邊 flag = 0 插入右邊
			if(flag == 1){
				flag = 0;
				output.insert(begin(output), *s);
			} 
			else{
				flag = 1;
				output.push_back(*s);
			}
		} 	
		print(output); 
	}
}
// 精簡版 又試了試傳遞容器引用
#include <bits/stdc++.h>
using namespace std;

struct Stu{
	string name;
	int height;
	bool operator < (Stu a) const{
		// 不相等按身高非遞減序 
		if(height != a.height)
			return height > a.height;
		// 否則返回名字升序 
		return name < a.name;
	}
};

void print(vector<Stu>& output){
	int first = 0;
	for(auto o : output)
		cout << (first++ ? " " : "") << o.name;
	cout << endl;
}

void queueUp(auto& s, int peo){
	vector<Stu> output;
	for(int i = 0, flag = 1; i < peo; i++, s++){
		// 中間到兩旁非增序 從中間先左後右交替入隊(反過來 
		// 第一個直接推入 併到處理下一個 
		if(i == 0) {
			output.push_back(*s);
			continue;
		}
		// flag = 1 插入左邊 flag = 0 插入右邊
		if(flag == 1){
			flag = 0;
			output.insert(begin(output), *s);
		} 
		else{
			flag = 1;
			output.push_back(*s);
		}
	} 
	// 排完一隊直接輸出
	print(output); 
}

int main() {
	// 讀取總人數N人總排數K排
	int n, k;
	set<Stu> stu;
	vector<Stu> output; 
	cin >> n >> k;
	
	// 讀取名字 身高
	for(int i = 0; i < n; i++) {
		string name; int height;
		cin >> name >> height;
		stu.insert({name, height}); 
	}
	
	// 算出最後一排多出來的人數
	int last = n - n / k * k;
	
	// 第一排要塞多出來的人數
	auto s = begin(stu);
	queueUp(s, n / k + last);
	
	// 輸出剩餘排 k - 1  重置flag 
	for(int j = 0; j < k - 1; j++){
		queueUp(s, n / k);
	}
}

// Bob175 Tom 188 Joe 190 Nick186
// Ann168 Mike170 Eva 168
// Tim160 Amy 160 John159

程式碼開頭有這個東西,這是當時用來思考排列出來的效果是怎麼樣的。因為當時不知道到底是按照什麼方式排序身高以及這些人在每一排裡面的位置。

通過這樣具體的模擬一下,標記每一個人的資料,就能夠更直觀看到題目需要實現的規律是什麼。雖然花了點時間,但是對於整個解題來講還是很重要的。

就像常常說道理解題意很重要,這裡顯然也是這麼回事。以後可以多多嘗試這樣的理解方式。


發現如果不想寫什麼函式傳參的型別,直接給一個 auto 讓編譯器自己推導就可以了,我們只需要負責將東西傳進去。如果要對內容進行更改而不是賦一份拷貝的話,那就加一個 & 即可。像下面的精簡版傳遞容器引用的時候,就在函式引數型別寫了一個 auto & 。


operator const ,當時過載 set 的自定義排序的時候不太記得怎麼寫,所以報了個錯誤。搜了下原來是忘了一個 const 常量關鍵字。放在引數型別的小括號 () const 後面即可。

還有就是雖然 devc++ 上如果你不寫 bool 的函式型別可以通過編譯檢測,並且還可以正確輸出。但 PAT 上不寫就直接編譯報錯了。不知道什麼原理,有什麼區別。

好像 lambda 函式寫的時候是可以根據返回值型別來推導函式型別的,不知道這個過載的是否也有這樣特性。

set過載


這裡用到了前幾題奇偶位置時候的交替輸出,設一個 flag 輸出一次改為 1 到另外的判斷又改為 0。


因為這裡涉及到兩頭插入,所以用了一個 vector 的 insert(),向容器任意位置插入元素,這裡就是首尾。push_back() 跟 output.insert(end(output), *s) 的效果是一樣的。

而這裡輸入的位置不是下標,而是地址的位置。

除了用 vector 向量以外還有另外一個容器是,deque 雙端佇列。它的函式就是 push_back() 和 push_front()。省去自己寫地址了。

參考程式碼


未精簡之前因為沒有采用函式,所以 vector 是共用的,但是每一行都需要重新排隊,所以需要清理前一排的內容,用到了 clear() 清除函式。

是 clear 而不是 clean,當時就寫錯了。