[BZOJ4790][构造] CERC 2016 D. Dancing Disks

CERC2016 D题

题意

有个6*6的棋盘,刚开始在(1,1)这个点有n个棋子叠起来(棋子大小无序),你可以从顶部移动任意多个棋子到相邻的位置,但是只能向下或者向右移动,要求你构造一种移动方法使所以棋子移动到(6,6)并且棋子大小从底向上降序

Sample
input
6
1 6 5 4 3 2
output
1 1 D 6
2 1 D 6
4 1 D 6
5 1 D 6
6 1 R 6
6 2 R 6
6 3 R 6
6 4 R 6
6 5 R 5
6 5 R 1


x y R/D w 表示从(x,y)顶部取w个棋子想右/下移动

一到机房就被Manchery安利了这题……
搜CERC2016可以搜到题面题解

考虑n路归并,可以从(1..x,1..y)的格子归并到(x,y)

这里写图片描述

——图片自CERC2016

(1,1)因为没有移动,所以只能保证顶部第一个棋子是有序的,(2,1)(1,2)可以通过比较(1,1)的顶部两个棋子来做到两个棋子的升序,(2,2)可以归并(1,1)(2,1)(1,2)做到5个有序……其他的格子可以从它之前的格子中n路归并得到序列。递归处理就可以了。
大概(6,6)刚好可以做到40000
要做到从底向上升序的话,之前的格子得从底向上降序,反之亦然

粗略算了下复杂度……大概 O(66+36nlog36) ?? 反正跑得过……

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <stack>
#include <queue>
#include <vector>
#define X first
#define Y second

using namespace std;

typedef pair<int,int> pari;
typedef pair<int,pari> par;

int n;
int C[10][10];
stack<int> A[10][10],B[10][10];
priority_queue<par,vector<par>,less<par> > Q1;
priority_queue<par,vector<par>,greater<par> > Q2;

inline void move(int i,int j,int x,int y){
    for(;i<x;i++) printf("%d %d D 1\n",i,j);
    for(;j<y;j++) printf("%d %d R 1\n",i,j);
}

inline void Sort1(int x,int y){
    for(int i=1;i<=x;i++)
        for(int j=1;j<=y;j++)
            if((i!=x||j!=y)&&!A[i][j].empty()) Q1.push(par(A[i][j].top(),pari(i,j)));
    while(!Q1.empty()){
        A[x][y].push(Q1.top().X);
        int i=Q1.top().Y.X,j=Q1.top().Y.Y; Q1.pop();
        move(i,j,x,y); A[i][j].pop();
        if(!A[i][j].empty()) Q1.push(par(A[i][j].top(),pari(i,j)));
    }
}

inline void Sort2(int x,int y){
    for(int i=1;i<=x;i++)
        for(int j=1;j<=y;j++)
            if((i!=x||j!=y)&&!A[i][j].empty()) Q2.push(par(A[i][j].top(),pari(i,j)));
    while(!Q2.empty()){
        A[x][y].push(Q2.top().X);
        int i=Q2.top().Y.X,j=Q2.top().Y.Y; Q2.pop();
        move(i,j,x,y); A[i][j].pop();
        if(!A[i][j].empty()) Q2.push(par(A[i][j].top(),pari(i,j)));
    }
}

void solve(int x,int y,int wh){
    if(x==1&&y==1){
        if(!B[x][y].empty()) A[x][y].push(B[x][y].top()),B[x][y].pop();
        return ;
    }
    if((x==1&&y==2)||(x==2&&y==1)){
        if(B[1][1].empty()) return ;
        int i=B[1][1].top(); B[1][1].pop();
        if(B[1][1].empty()){ A[x][y].push(i); move(1,1,x,y); return ; }
        int j=B[1][1].top(); B[1][1].pop();
        if((i>j)^wh) printf("%d %d %c 2\n",1,1,x==1?'R':'D');
        else printf("%d %d %c 1\n%d %d %c 1\n",1,1,x==1?'R':'D',1,1,x==1?'R':'D');
        if(wh) A[x][y].push(max(i,j)),A[x][y].push(min(i,j));
        else A[x][y].push(min(i,j)),A[x][y].push(max(i,j));
        return ;
    }
    for(int i=x;i;i--)
        for(int j=y;j;j--)
            if(i!=x||j!=y) solve(i,j,wh^1);
    if(wh) Sort1(x,y);
    else Sort2(x,y);
}

int main(){
    freopen("disks.in","r",stdin);
    freopen("disks.out","w",stdout);
    scanf("%d",&n);
    for(int i=1,x;i<=n;i++){
        scanf("%d",&x); B[1][1].push(x);
    }
    solve(6,6,1);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值