[C++]Saving the Universe——Google Code Jam Qualification Round 2008

Google Code Jam 2008 资格赛的第一题:Saving the Universe。

问题描述如下:

Problem

The urban legend goes that if you go to the Google homepage and search for "Google", the universe will implode. We have a secret to share... It is true! Please don't try it, or tell anyone. All right, maybe not. We are just kidding.

The same is not true for a universe far far away. In that universe, if you search on any search engine for that search engine's name, the universe does implode!

To combat this, people came up with an interesting solution. All queries are pooled together. They are passed to a central system that decides which query goes to which search engine. The central system sends a series of queries to one search engine, and can switch to another at any time. Queries must be processed in the order they're received. The central system must never send a query to a search engine whose name matches the query. In order to reduce costs, the number of switches should be minimized.

Your task is to tell us how many times the central system will have to switch between search engines, assuming that we program it optimally.

Input

The first line of the input file contains the number of cases, NN test cases follow.

Each case starts with the number S -- the number of search engines. The next S lines each contain the name of a search engine. Each search engine name is no more than one hundred characters long and contains only uppercase letters, lowercase letters, spaces, and numbers. There will not be two search engines with the same name.

The following line contains a number Q -- the number of incoming queries. The next Qlines will each contain a query. Each query will be the name of a search engine in the case.

Output

For each input case, you should output:

Case #X: Y

where X is the number of the test case and Y is the number of search engine switches. Do not count the initial choice of a search engine as a switch.

Limits

0 < N ≤ 20

Small dataset

2 ≤ S ≤ 10

0 ≤ Q ≤ 100

Large dataset

2 ≤ S ≤ 100

0 ≤ Q ≤ 1000

Sample


Input 

Output 
2
5
Yeehaw
NSM
Dont Ask
B9
Googol
10
Yeehaw
Yeehaw
Googol
B9
Googol
NSM
B9
NSM
Dont Ask
Googol
5
Yeehaw
NSM
Dont Ask
B9
Googol
7
Googol
Dont Ask
NSM
NSM
Yeehaw
Yeehaw
Googol

Case #1: 1
Case #2: 0

In the first case, one possible solution is to start by using Dont Ask, and switch to NSM after query number 8.

For the second case, you can use B9, and not need to make any switches.

—————————————————————————————————————————————————————————————————————————————

简言之,就是进行N轮测试,每次测试先给出S个搜索引擎的名字,再给出Q条搜索内容(都是搜索引擎的名字);

为了不让搜索引擎搜索自己的名字(因为这样会爆炸),系统需要切换搜索引擎,给出每次的测试需要切换搜索引擎的最小次数。


我的想法主要是使用map<string,int>保存每次读取的名字,map对于Key相同的元素只会保存一个。

假如在插入“query”后map的大小等于了搜索引擎的数量S,表明前S-1个种类的名字应该由当前名字("query")的搜索引擎来搜索。这时候系统就把搜索引擎从“query”切换成另一个搜索引擎,清除map并插入“query”,然后循环进行。


后来看到Google官方给出的答案思路也是类似的,只是用了set<string>来保存元素,每次操作会先在set中查找该元素的位置。


下面是我用C++的解答。

#include<iostream>
#include<fstream>
#include<string>
#include<map>

using namespace std;

int main(){
	ifstream readFile("A-large-practice.in");
	if (!readFile){
		cout << "Open file failed!" << endl;
		return 0;
	}
	ofstream ofs("A-large-pratice.out");
	int N;	//the number of cases
	int S;	//the number of search engines
	readFile >> N;
	int* count = new int[N];	//store results

	for (int i = 0; i < N; i++){
		//read in search engines' name
		(readFile >> S).get();

		for (int i = 0; i < S; i++){
			string engine;
			getline(readFile, engine);
		}

		int Q;
		(readFile >> Q).get();	//the number of incoming queries
		map<string,int> find_list;
		int switch_count = 0;
		find_list.clear();
		for (int j = 0; j < Q; j++){
			string query;
			getline(readFile, query); 
			find_list[query] = 1;
			if (find_list.size() == S) { //list is full of all kinds of engines
				find_list.clear();
				find_list[query] = 1;
				switch_count++; //It's one switch 
			}
		}
		count[i] = switch_count;
	}
	for (int i = 0; i < N; i++){
		ofs << "Case #" << i + 1 << ": " << count[i] << endl;
	}

	delete[]count;
	readFile.close();
<span style="white-space:pre">	</span>ofs.close();
	return 0;
}


### 问题解析 P13454 [GCJ 2008 Qualification] Saving the UniverseGoogle Code Jam 2008 资格赛中的一个经典编程问题。问题的核心是通过最少的切换次数来模拟搜索引擎的查询处理过程,使得所有查询都能被正确处理。 问题描述大致如下: - 有若干个搜索引擎,每个搜索引擎可以处理一组查询。 - 给定一个查询列表,要求在不使用某个搜索引擎处理它的情况下,尽可能少地切换搜索引擎。 - 每次切换搜索引擎时,需要重新加载一个新的搜索引擎来处理后续查询。 ### 解题思路 1. **贪心策略**: - 在遍历查询列表时,维护当前使用的搜索引擎。 - 当遇到当前搜索引擎无法处理的查询时,需要切换搜索引擎。 - 切换时,选择下一个最远的查询作为新的搜索引擎,以最大化当前引擎的使用范围。 2. **具体实现**: - 使用一个集合来存储当前可用的搜索引擎。 - 遍历查询列表,并记录每个查询的下一次出现位置。 - 当当前引擎无法处理当前查询时,选择下一个最远的查询作为新的引擎,并增加切换次数。 3. **时间复杂度**: - 遍历查询列表的时间复杂度为 $O(N)$,其中 $N$ 是查询的数量。 - 预处理每个查询的下一次出现位置的时间复杂度为 $O(N)$。 ### 示例代码 以下是一个基于贪心策略的实现: ```cpp #include <iostream> #include <vector> #include <map> #include <set> #include <algorithm> using namespace std; int main() { int T; cin >> T; for (int caseNum = 1; caseNum <= T; ++caseNum) { int S, Q; cin >> S >> Q; // 读取搜索引擎列表 vector<string> engines(S); for (int i = 0; i < S; ++i) { cin >> engines[i]; } // 读取查询列表 vector<string> queries(Q); for (int i = 0; i < Q; ++i) { cin >> queries[i]; } // 预处理每个查询的下一次出现位置 map<string, vector<int>> nextOccurrence; for (const string& engine : engines) { nextOccurrence[engine] = vector<int>(Q, -1); int last = -1; for (int i = Q - 1; i >= 0; --i) { if (queries[i] == engine) { last = i; } nextOccurrence[engine][i] = last; } } int switches = 0; int currentIndex = 0; string currentEngine = ""; while (currentIndex < Q) { // 找到当前可以使用的最佳引擎 string bestEngine = ""; int farthest = -1; for (const string& engine : engines) { if (engine == currentEngine) continue; int nextPos = nextOccurrence[engine][currentIndex]; if (nextPos > farthest) { farthest = nextPos; bestEngine = engine; } } if (bestEngine == "") { // 所有引擎都无法处理当前查询,直接跳过 break; } // 切换引擎,并更新当前索引 currentEngine = bestEngine; switches++; currentIndex = farthest; } cout << "Case #" << caseNum << ": " << switches << endl; } return 0; } ``` ### 代码说明 - **输入处理**:读取测试用例数、搜索引擎数量和查询数量。 - **预处理**:为每个搜索引擎预处理其在查询列表中的下一次出现位置。 - **贪心选择**:在每次切换时,选择能够处理最多后续查询的引擎。 - **输出结果**:输出每个测试用例的最小切换次数。 ### 总结 该问题通过贪心策略实现了高效的解决方案,能够在大规模数据下保持良好的性能。通过预处理查询的下一次出现位置,可以快速找到最优的切换点,从而减少不必要的计算。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值