解析
应该从大到小一个个移,这样后面大盘就可以直接忽略,保证没有冗余操作,必定最优(如果先移动小的,后面移动大的时还要动小的)
对于第id个从当前位置到目标的移动有两种移动方案:
法1:把id-1个移到中转站,把id移到目标
法2:把id-1个移到id的目标,把id移到中转站,把id-1个都移到id原位置,把id移到目标
洛谷很多题解都只考虑了第一种方案,但在加入数据11后会被卡掉
法2虽然看起来繁琐,但其好处是第id-1个会落到id的原位置,当其目标恰好是这个位置,且其原位置在id的目标(这样法2的第一步就不用处理id-1)时,我们只需要处理一次id-1的移动,而用法1需处理2次,故此时法2更优
代码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int place[50020],now,goal,ans[50020];
int n;
char s[10]={'m','A','B','C','f','u','c','k'};
long long tot=0;
void move(int id,int to){if(place[id]==to) return;int spj=0;int trans=6-place[id]-to;if(place[id-1]!=to) spj=1;//只有当下一个盘(id-1)目前在id的目标且其目标在id目前的位置时触发特判 if(ans[id-1]!=place[id]) spj=1;//if(id==1) spj=1; 这行似乎不加也行 if(spj==0){//spj:方法2 for(int i=id-1;i>=1;i--) move(i,to);printf("move %d from %c to %c\n",id,s[place[id]],s[trans]);tot++;place[id]=trans;for(int i=id-1;i>=1;i--) move(i,6-place[id]-to);printf("move %d from %c to %c\n",id,s[place[id]],s[to]);tot++;place[id]=to;return;}//正常:方法1 for(int i=id-1;i>=1;i--) move(i,6-place[id]-to);printf("move %d from %c to %c\n",id,s[place[id]],s[to]);place[id]=to;++tot;
}
int main(){int num;scanf("%d",&n);for(int i=1;i<=3;i++){scanf("%d",&num);for(int j=1;j<=num;j++){scanf("%d",&now);place[now]=i;}}for(int i=1;i<=3;i++){scanf("%d",&num);for(int j=1;j<=num;j++){scanf("%d",&goal);ans[goal]=i;}}//if(n==3&&place[1]==3&&ans[1]==1){//printf("move 3 from A to B\n");//place[3]=2;//tot++;//}for(int i=n;i>=1;i--){if(place[i]==ans[i]) continue;move(i,ans[i]);}printf("%lld",tot);return 0;
}
反数据大全
自己调时洛谷基本都能过(too water。。)
但自己又想到一些反数据卡掉了一些不成熟的想法(基于数据11)
如果洛谷和这些数据你都能过,那么基本没问题啦~~
(限于篇幅输出只显示步数)
case 1
in:
3
1 3
0
2 2 1
2 2 1
0
1 3
out:
5
case 2
in:
4
1 3
1 4
2 2 1
2 2 1
1 4
1 3
out:
5
case 3
in:
3
1 3
0
2 2 1
1 2
1 1
1 3
out:
6
case 4
in:
4
2 4 1
0
2 3 2
3 3 2 1
0
1 4
out:
10
Ending
你又变厉害了呢~~~
祝大家AKNOI,rp++!!