Xtreme 10.0 - Painter's Dilemma
题目:
https://www.hackerrank.com/contests/ieeextreme-challenges/challenges/painters-dilemma
题意:
Bob是一名粉刷匠,他的工作是帮别人粉刷房子的墙壁。Bob只有两把刷子,在工作时,需要根据工序给房子刷不同的颜色。如果要刷的颜色和刷子现有的颜色都不同,Bob必须挑选一把刷子进行清洗并重新上色。现给出房子粉刷工序颜色的序列,求出一种给刷子换色的方案,使得Bob给刷子换色的次数最少,并输出换色的次数。
输入:
第一行 输入 t,代表有t个测试用例,其中 1 <= t <= 5。
第二行输入 n,代表粉刷序列的长度,其中 1 <= n <= 500。
第三行输入n个数c[1], c[2], ..., c[n - 1],代表需要刷墙颜色的序列,其中 1 <= c[i] <= 20,c[i]每个整数的取值代表一种颜色。
接下来是第2个测试用例的n.....
输出:Bob给刷子换色的最少次数。
测试用例:
输入:
2
5
7 7 2 11 7
10
9 1 7 6 9 9 8 7 6 7
输出:
3
6
解释:
对于第一个测试用例,刷7号颜色时,Bob给第一把刷子上7号色;刷第二个7号色时,不用换色;刷2号色时,Bob给第二把刷子上2号色;刷11号色时,Bob清洗第二把刷子并重新上11号色;刷最后一个7号色时,Bob直接用第一把刷子,不用换色。因此总共换色次数是3。
思路:
这道题实际上是操作系统中页面置换的问题。Bob的两把刷子可以看做内存的页数,颜色序列可以看做进程要访问的页面,Bob重新给刷子上色可以看做缺页中断。由于要上色的颜色序列是已知的,因此可以使用页面调度的OPT算法来解决。该算法的思想是,当发生缺页中断时,把将来距离现在最远的访问页面调出内存。
注意,在Bob需要给刷子时,需要找到将来最远使用到的颜色,来进行置换。该搜寻的方法可以通过遍历颜色序列来求得。但时间复杂度为O(n^2)。由于题目中颜色的种类是有限的(20种),因此可以在一开始对颜色序列进行预处理,先创建20个颜色队列表,将每种颜色的出现的位置存到对应的队列中,则在需要查找最远出现颜色的时,直接查表就可以了。这样可以将时间复杂度降为O(n)。
代码:
#include <cstdio>
#include <vector>
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
#define EMPTY -1
void oneCase() {
int n = 0;
cin >> n;
vector<int> seq(n);
int cache[2] = { EMPTY, EMPTY };
for (int i = 0; i < n; i++) {
cin >> seq[i];
}
vector<queue<int>> table(21);
for (int i = 0; i < n; i++) {
table[seq[i]].push(i);
}
int resCount = 0;
for (int i = 0; i < n; i++) {
if (seq[i] != cache[0] && seq[i] != cache[1]) {
resCount++;
if (cache[0] == EMPTY) {
cache[0] = seq[i];
table[seq[i]].pop();
continue;
}
if (cache[1] == EMPTY) {
cache[1] = seq[i];
table[seq[i]].pop();
continue;
}
int tmp0 = table[cache[0]].empty() ? 501 : table[cache[0]].front();
int tmp1 = table[cache[1]].empty() ? 501 : table[cache[1]].front();
if (tmp0 < tmp1) {
//out 1;
cache[1] = seq[i];
} else {
//out 0;
cache[0] = seq[i];
}
}
table[seq[i]].pop();
}
cout << resCount << endl;
}
int main() {
int caseCount = 0;
cin >> caseCount;
while (caseCount) {
oneCase();
caseCount--;
}
}