loj3388. 「NOIP2020」移球游戏
好题。这个思路是嫖来的(参见这里)
对于每一种颜色分开处理。考虑将是当前颜色的球视为 ,否则视为 ,然后先构造一个全 列,具体就是先统计第一列 的个数 ,然后第 列移动 个球到第 列,然后分解第一列, 放在第 列, 放在第 列。然后把 列的 个 扔回一号柱子,然后同样的方法分解第 列, 扔到第一列, 扔到第 列(如果第一列满了就全扔到最后一列)。由于 最多 个,前两个柱子上至少有 个 因此肯定可以填满第一列。
有了全 列考虑构造全 列。先把全 列换到第 列,最后一列空,我们用同样的方法拆解第一列,这样我们会发现第一列的 都到了第 列的顶端,第 列出现了一个新的全 列。于是可以用新的全 列重复这个过程,最后所有的 就都在列的顶端了,直接全部提出来到空柱子上面即可,最后用全 列去补上空位,然后递归处理即可。
但是我们发现当只剩两种颜色的时候这个过程就无法进行了,因为柱子不够。这个时候直接特判讨论掉就好了。
最坏情况大约只需要 次操作。
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
int n,m,p[61];
vector<int> v[61];
vector<pair<int,int> > ans;
inline int read()
{
int x=0;
char c=getchar();
while(c<'0'||c>'9')
c=getchar();
while(c>='0'&&c<='9')
{
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x;
}
void print(int x)
{
if(x>=10)
print(x/10);
putchar(x%10+'0');
}
inline void move(int x,int y)
{
ans.push_back(make_pair(x,y));
v[y].push_back(v[x].back());
v[x].pop_back();
}
inline int solve(int x,int col)
{
int cnt=0;
for(auto i:v[x])
cnt+=i==col;
return cnt;
}
inline void pt()
{
print((int)ans.size());
putchar('\n');
for(auto i:ans)
{
print(i.first);
putchar(' ');
print(i.second);
putchar('\n');
}
}
inline void make0(int n,int col)
{
int cnt=solve(p[1],col);
for(register int i=1;i<=cnt;++i)
move(p[n],p[n+1]);
while(!v[p[1]].empty())
if(v[p[1]].back()==col)
move(p[1],p[n]);
else
move(p[1],p[n+1]);
for(register int i=1;i<=m-cnt;++i)
move(p[n+1],p[1]);
while(!v[p[2]].empty())
if(v[p[2]].back()==col||(int)v[p[1]].size()==m)
move(p[2],p[n+1]);
else
move(p[2],p[1]);
p[1]^=p[n]^=p[1]^=p[n];
p[2]^=p[n+1]^=p[2]^=p[n+1];
}
inline void make1(int n,int pos,int col)
{
int cnt=solve(p[pos],col);
for(register int i=1;i<=cnt;++i)
move(p[n],p[n+1]);
while(!v[p[pos]].empty())
if(v[p[pos]].back()==col)
move(p[pos],p[n]);
else
move(p[pos],p[n+1]);
p[pos]^=p[n+1]^=p[pos]^=p[n+1];
p[pos]^=p[n]^=p[pos]^=p[n];
}
inline void work(int n,int col)
{
for(register int i=1;i<n;++i)
while(v[p[i]].back()==col)
move(p[i],p[n+1]);
for(register int i=1;i<n;++i)
while((int)v[p[i]].size()<m)
move(p[n],p[i]);
}
int main()
{
n=read(),m=read();
for(register int i=1;i<=n;++i)
for(register int j=1;j<=m;++j)
v[i].push_back(read());
if(n==2)
{
int cnt=solve(1,1),tmp=0;
for(register int i=1;i<=cnt;++i)
move(2,3);
while(!v[1].empty())
if(v[1].back()==1)
{
move(1,2);
++tmp;
}
else
move(1,3);
for(register int i=1;i<=tmp;++i)
move(2,1);
for(register int i=tmp+1;i<=m;++i)
move(3,1);
for(register int i=1;i<=cnt;++i)
move(3,2);
for(register int i=tmp+1;i<=m;++i)
move(1,3);
while(!v[2].empty())
if(v[2].back()==1)
move(2,1);
else
move(2,3);
pt();
return 0;
}
for(register int i=1;i<=n+1;++i)
p[i]=i;
for(register int i=n;i>=3;--i)
{
make0(i,i);
for(register int j=1;j<i;++j)
make1(i,j,i);
work(i,i);
}
int cnt=solve(p[1],1),tmp=0;
for(register int i=1;i<=cnt;++i)
move(p[2],p[3]);
while(!v[p[1]].empty())
if(v[p[1]].back()==1)
{
move(p[1],p[2]);
++tmp;
}
else
move(p[1],p[3]);
for(register int i=1;i<=tmp;++i)
move(p[2],p[1]);
for(register int i=tmp+1;i<=m;++i)
move(p[3],p[1]);
for(register int i=1;i<=cnt;++i)
move(p[3],p[2]);
for(register int i=tmp+1;i<=m;++i)
move(p[1],p[3]);
while(!v[p[2]].empty())
if(v[p[2]].back()==1)
move(p[2],p[1]);
else
move(p[2],p[3]);
pt();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· .NET Core 中如何实现缓存的预热?
· 三行代码完成国际化适配,妙~啊~
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?