题解:P10191 [USACO24FEB] Test Tubes S
题解:P10191 [USACO24FEB] Test Tubes S
前言
小小思维题。
题意简述
你有三个栈,每次可以将一个栈顶端所有颜色相同的数移动到另外一个栈顶。\ 最开始只有第一个和第二个栈有数,并且只有两种。求让第一个栈和第二个栈中的数都相同的最小操作数,并给出操作方法(并且需要保证第三个栈中没有数)。
思路
这里令三个栈分别为
注意到每次移动只能将连续的相同颜色的数移动,那么我们就把连续相同颜色的数捆在一起。这样可以方便我们的操作。
那么如果我们将某个栈顶移动到的另外一个栈顶,且它们颜色相同,可以视作弹出了前者的栈顶。
于是有个重要结论,
证明如下:
可能不太严谨。首先如果
接着是中间的消除过程。
显然如果
但是控制在 1 以内有什么理由吗?如果不控制在 1 之内,就会出现某一个栈空了,但另外一个栈就像夹心蛋糕一样,此时的消除就会花费花费很多额外的代价。而控制在 1 之内就没有这些代价,因为最后的状态十分简洁。
现在就在于处理终止状态。根据上面的操作方法,条件不难得出。
显然终止条件只能是
此时分类讨论一波,
如果不同,就讨论是否
讨论完之后,你就做完了。
CODE
#include <bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define se second
#define pb emplace_back
using namespace std;
constexpr int N=1e5+5;
int n,typ;
int a[N],b[N],c[N];
int ida,idb,idc;
vector<pii>ans;
void solve()
{
scanf("%d%d",&n,&typ);
ans.clear(),ida=idb=idc=0;
for(int i=1,x;i<=n;i++)
{
scanf("%1d",&x);
if(x!=a[ida])a[++ida]=x;
}
for(int i=1,x;i<=n;i++)
{
scanf("%1d",&x);
if(x!=b[idb])b[++idb]=x;
}
while(ida+idb>2)
{
if(ida && idb && a[ida]==b[idb])
{
if(ida>=idb)ans.pb(1,2),ida--;
else ans.pb(2,1),idb--;
continue;
}
if(idc)
{
if(ida && a[ida]==c[idc])ans.pb(1,3),ida--;
else if(idb && b[idb]==c[idc])ans.pb(2,3),idb--;
}
else
{
if(ida>=idb)ans.pb(1,3),c[++idc]=a[ida--];
else ans.pb(2,3),c[++idc]=b[idb--];
}
}
if(a[ida]==b[idb])
{
ans.pb(1,2);
if(idc)ans.pb(3,1);
}
else if(idc)
{
// cerr<<"1:";
if(ida!=idb)
{
// cerr<<"1:";
if(ida==2)ans.pb(1,2),b[++idb]=a[ida--];
else ans.pb(2,1),a[++ida]=b[idb--];
}
if(c[idc]==a[ida])ans.pb(3,1);
else ans.pb(3,2);
}
else
{
// cerr<<"2:";
if(ida==2)ans.pb(1,2);
else if(idb==2)ans.pb(2,1);
}
printf("%d\n",(int)ans.size());
if(typ==1)return;
for(auto [x,y]:ans)printf("%d %d\n",x,y);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("data.in","r",stdin);
#endif
int T;scanf("%d",&T);
while(T--)solve();
return 0;
}
看在讲的如此详细的份上,点个赞再走吧!