算法思路与可视化讲解
思考: 如果试管里有 111221,倒出最上面的 1 和倒出三个 1 是一次操作吗?
是的! 只要是连续的相同颜色,就可以一次性倒完。
void dedup(vector &t, char *s) {
t.clear();
for (int i = 0; i < n; i++) {
if (i == 0 || t[t.size()-1] != s[i]-'1')
t.push_back(s[i]-'1');
}
}
Top(A) == Top(B):将元素个数较多的栈顶倒入较少的栈(合并消除)。
Top(A) == Top(C) 或 Top(B) == Top(C):将匹配的栈顶倒入 C 中合并。
若 Size(A)==1 且 Size(B)==1 且栈顶异色:
$\to$ 将 C (若有) 倒回匹配的管子。
$\to$ 大功告成。
若 A 为空 (B 同理):
1. 若 Size(B) == 1: C 倒给 A,结束。
2. 否则: B 栈顶给 A (拆分),继续循环。
状态:Top(A)=2 != Top(B)=1。C为空。
操作:将 B(1) 移入 C。
状态:Top(A)=2 == Top(B)=2。
操作:A 比 B 高,将 A(2) 倒入 B (合并)。
状态:A, B 各剩1层且异色(边界情况)。
操作:将 C(1) 倒回 A。
完成!A 全 1,B 全 2。
// 策略 1: 顶部颜色相同,进行合并
if (!ts[0].empty() && !ts[1].empty() && ts[0].back() == ts[1].back()) {
// 贪心:把较高的那一管倒入较矮的,减少层数
if (ts[0].size() > ts[1].size()) {
ts[0].pop_back();
ans.push_back({1, 2});
} else {
ts[1].pop_back();
ans.push_back({2, 1});
}
continue;
}
// 边界情况 B: 移动到空管
// 如果某管是空的,且另一管不只一层,或者那一层不该待在那
if (ts[0].empty() && ts[1].size() > 1 && ts[1].back() != beaker ||
ts[1].empty() && ts[0].size() > 1 && ts[0].back() != beaker) {
int i = ts[0].empty() ? 0 : 1; // 目标是空管 i
ts[i].push_back(ts[1-i].back());
ts[1-i].pop_back();
ans.push_back({(1-i)+1, i+1});
}
// 边界情况 A: 结束判断与烧杯回填
if (ts[0].size() <= 1 && ts[1].size() <= 1) {
if (beaker != -1) {
// 烧杯非空,倒回匹配的管子
// 若 T0 是空的或者 T0 颜色和烧杯一样,就倒回 T0
int i = (ts[0].empty() || ts[0].back() == beaker) ? 0 : 1;
ans.push_back({3, i+1});
}
break; // 完成
}
// 策略 2/3: 借用/填入烧杯
int i;
if (beaker == -1) i = ts[0].size() > ts[1].size() ? 0 : 1; // 烧杯空,取高的
else i = ts[0].back() == beaker ? 0 : 1; // 烧杯非空,取匹配的
beaker = ts[i].back();
ts[i].pop_back();
ans.push_back({i+1, 3});
#include <bits/stdc++.h>
using namespace std;
int t,n,p,m;
char s1[100005], s2[100005];
vector<int> ts[2];
using pi = pair<int,int>;
vector<pi> ans;
int beaker;
void dedup(vector<int> &t, char *s) {
t.clear();
for (int i = 0; i < n; i++) {
if (i == 0 || t[t.size()-1] != s[i]-'1')
t.push_back(s[i]-'1');
}
}
int main() {
scanf("%d", &t);
while (t--) {
scanf("%d %d", &n, &p);
scanf("%s", s1);
scanf("%s", s2);
// dedup the colors
dedup(ts[0], s1);
dedup(ts[1], s2);
beaker = -1;
ans.clear();
while (1) {
// merge the same color to the lower tube
if (!ts[0].empty() && !ts[1].empty() && ts[0].back() == ts[1].back()) {
if (ts[0].size() > ts[1].size()) {
ts[0].pop_back();
ans.push_back({1, 2});
} else {
ts[1].pop_back();
ans.push_back({2, 1});
}
continue;
}
// move to an empty tube
if (ts[0].empty() && ts[1].size() > 1 && ts[1].back() != beaker ||
ts[1].empty() && ts[0].size() > 1 && ts[0].back() != beaker) {
int i = ts[0].empty() ? 0 : 1;
ts[i].push_back(ts[1-i].back());
ts[1-i].pop_back();
ans.push_back({(1-i)+1, i+1});
}
// done if both have <= 1, move beaker liquid back if necessary
if (ts[0].size() <= 1 && ts[1].size() <= 1) {
if (beaker != -1) {
int i = (ts[0].empty() || ts[0].back() == beaker) ? 0 : 1;
ans.push_back({3, i+1});
}
break;
}
// move liquid from tube to beaker
int i;
if (beaker == -1)
i = ts[0].size() > ts[1].size() ? 0 : 1;
else
i = ts[0].back() == beaker ? 0 : 1;
beaker = ts[i].back();
ts[i].pop_back();
ans.push_back({i+1, 3});
}
printf("%d\n", (int)ans.size());
if (p > 1) {
for (auto &a: ans)
printf("%d %d\n", a.first, a.second);
}
}
return 0;
}