P7115 [NOIP2020] 移球游戏
link
这道题我觉得是我做到过极好的构造题了,思路和优化的方法都比较有特点,对数据点范围的分析已经对数据的给分也比较恰到好处。之前做了这道题,特此写一篇题解。
首先要批判一下给的小样例,对解题很容易起到反作用。所以构造题不能只看样例,要自己去手搓一下,这样才方便去做。
本题我们可以把数据点分为三个部分,其对应分别为N=2的部分,时间复杂度为
Sub1:n=2
看到这道题,我们就应该想到一道题面看上去极其相似的题目:汉诺塔。于是我们可以仿照汉诺塔的思路,找规律来完成此题。
此时我们假设有三根柱子
此时我们想要把1号球全部移到一根柱子上。假设现在有
Step1:将
Step2:对
Step3:将
Step4:将
Step5:将
Step6:将
Step7:对
这样,七步操作结束后,我们可以得到
Sub2
我们的目的是整理好n根柱子上的球。此时我们可以一根一根的去做,从n到2依次递归完成。
具体做法:
假设我们现在要整理好第
Step1:枚举 :从1到n 目的:将所有颜色为 的球移到该柱子的顶端;
更具体一点的操作:
(一):将
(二):对
(三):将
(四):将
(五):将
Step2:枚举 :将 柱顶端为 的球移到 柱上。
Step3:对 柱上每一个球进行分讨。具体的说,若当前球颜色为 则将其移动至 柱上,否则,则将其随便移动到一个未满的柱子上。
然后我们递归去做每一次。一共要递归
Sub3
考虑对时间复杂度进行优化。上述过程可以分为两个部分去看,分别为执行操作与递归。发现操作的复杂度不好优化,选择优化递归。考虑多根颜色一起做,即分治算法。
建立一个
即:不存在一根柱子上同时存在颜色大于等于
如何区分:每轮取出
分治总共要
这真是一道不可多得的构造好题娃。
哦还有代码
CODE
点击查看代码
#include<bits/stdc++.h>
//#define int long long
#define pa pair<int,int>
#define x first
#define y second
#define pb push_back
#define mp make_pair
using namespace std;
const int N=60,M=410,Q=820001;
int n,m;
int em;
int top[N],a[N][M];
bool impx[M],impy[M];
int T;
pa ans[Q];
void move(int x,int y)
{
ans[++T]=mp(x,y);
a[y][++top[y]]=a[x][top[x]--];
}
int merge(int x,int y,int mid)
{
int cx=0,cy=0;
for(int i=1;i<=m;++i)
impx[i]=(a[x][i]<=mid),impy[i]=(a[y][i]<=mid),
cx+=impx[i],cy+=impy[i];
if(cx+cy>m)
{
cx=m-cx,cy=m-cy;
for(int i=1;i<=m;++i)
impx[i]^=1,impy[i]^=1;
}
for(int i=1;i<=m;++i)
if(!impx[i] && cx+cy<m)
impx[i]=1,++cx;
for(int i=cx;i;--i) move(y,em);
for(int i=m;i;--i)
if(impx[i]) move(x,y);
else move(x,em);
for(int i=m-cx;i;--i) move(em,x);
for(int i=cx;i;--i) move(y,x);
for(int i=cx;i;--i) move(em,y);
for(int i=cx;i;--i) move(x,em);
for(int i=m;i;--i)
if(impy[i]) move(y,em);
else move(y,x);
int p=em;
em=y;
return p;
}
void solve(int l,int r)
{
if(l==r) return;
int mid=l+r>>1;
vector<int> now;
for(int i=1;i<=n+1;++i)
{
if(i==em) continue;
if(l<=a[i][1] && a[i][1]<=r)
now.pb(i);
}
for(int i=0;i+1<now.size();++i)
now[i+1]=merge(now[i],now[i+1],mid);
solve(l,mid),solve(mid+1,r);
}
signed main()
{
freopen("ball.in", "r", stdin);
freopen("ball.out", "w", stdout);
// ios::sync_with_stdio(false);
// cin.tie(0);cout.tie(0);
scanf("%d%d",&n,&m);
em=n+1;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
scanf("%d",&a[i][j]);
for(int i=1;i<=n;i++) top[i]=m;
solve(1,n);
printf("%d\n",T);
for(int i=1;i<=T;++i)
printf("%d %d\n",ans[i].first,ans[i].second);
return 0;
}
哦,留个问题:为什么这道题关闭了输入输出流的cin,cout会比scanf,printf慢那么多?差点洛谷上过不了~~~(我太鶸了)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!