LG P7115 移球游戏

Description

小 C 正在玩一个移球游戏,他面前有 $n + 1$ 根柱子,柱子从 $1 \sim n + 1$ 编号,其中 $1$ 号柱子、$2$ 号柱子、……、$n$ 号柱子上各有 $m$ 个球,它们自底向上放置在柱子上,$n + 1$ 号柱子上初始时没有球。这 $n \times m$ 个球共有 $n$ 种颜色,每种颜色的球各 $m$ 个。

初始时一根柱子上的球可能是五颜六色的,而小 C 的任务是将所有同种颜色的球移到同一根柱子上,这是唯一的目标,而每种颜色的球最后放置在哪根柱子则没有限制。

小 C 可以通过若干次操作完成这个目标,一次操作能将一个球从一根柱子移到另一根柱子上。更具体地,将 $x$ 号柱子上的球移动到 $y$ 号柱子上的要求为:

1. $x$ 号柱子上至少有一个球;
2. $y$ 号柱子上至多有 $m - 1$ 个球;
3. 只能将 $x$ 号柱子最上方的球移到 $y$ 号柱子的最上方。

小 C 的目标并不难完成,因此他决定给自己加加难度:在完成目标的基础上,使用的操作次数不能超过 $820000$。换句话说,小 C 需要使用至多 $820000$ 次操作完成目标。

小 C 被难住了,但他相信难不倒你,请你给出一个操作方案完成小 C 的目标。合法的方案可能有多种,你只需要给出任意一种,题目保证一定存在一个合法方案。

Solution

当仅有两个柱子时,有这样的方案:

钦定A柱上放置1号球,B柱上放置2号球,设A柱上原有$s$个1号球

1.从B柱向C柱移动$s$个球

2.将A柱上1号球置于B柱,2号球置于C柱

3.从B柱向A柱移动$s$个球,从C柱向A柱移动$m-s$个球(以上的操作是为了给A柱排序)

4.从B柱向C柱移动$m-s$个球(此时B柱空)

5.从A柱向B柱移动$m-s$个球(此时A,B柱都符合要求但不满)

6.从C柱按编号向A,B柱移动

总操作次数为$5m-s$

有多个柱时考虑分治,设阈值为$\frac{l+r}{2}$,小于阈值的定为1号球,其余的为2号球,$O(n^2)$地枚举两根柱子进行操作使至少一根柱子满足要求

总操作次数$5mn\log n$

时间复杂度$O(5mn^2\log n)$

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int n,m,sta[55][405],top[55],sum,ans[820005][2],tot;
bool tag[55];
inline int read()
{
    int f=1,w=0;
    char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') w=(w<<1)+(w<<3)+ch-'0',ch=getchar();
    return f*w;
}
void tran(int x,int y)
{
    ans[++tot][0]=x,ans[tot][1]=y,sta[y][++top[y]]=sta[x][top[x]--];
}
void solve(int l,int r)
{
    if(l==r) return;
    int mid=l+r>>1;
    memset(tag,false,sizeof(tag));
    for(int i=l;i<=mid;i++) for(int j=mid+1;j<=r;j++)
    {
        if(tag[i]||tag[j]) continue;
        sum=0;
        for(int k=1;k<=m;k++) sum+=(sta[i][k]<=mid)+(sta[j][k]<=mid);
        if(sum>=m)
        {
            sum=0;
            for(int k=1;k<=m;k++) sum+=(sta[i][k]<=mid);
            for(int k=1;k<=sum;k++) tran(j,n+1);
            for(int k=1;k<=m;k++)
                if(sta[i][top[i]]<=mid) tran(i,j);
                else tran(i,n+1);
            for(int k=1;k<=sum;k++) tran(j,i);
            for(int k=1;k<=m-sum;k++) tran(n+1,i);
            for(int k=1;k<=m-sum;k++) tran(j,n+1);
            for(int k=1;k<=m-sum;k++) tran(i,j);
            for(int k=1;k<=m;k++)
                if(sta[n+1][top[n+1]]<=mid&&top[i]<m) tran(n+1,i);
                else tran(n+1,j);
            tag[i]=true;
        }
        else
        {
            sum=0;
            for(int k=1;k<=m;k++) sum+=(sta[j][k]>mid);
            for(int k=1;k<=sum;k++) tran(i,n+1);
            for(int k=1;k<=m;k++)
                if(sta[j][top[j]]>mid) tran(j,i);
                else tran(j,n+1);
            for(int k=1;k<=sum;k++) tran(i,j);
            for(int k=1;k<=sum;k++) tran(n+1,j);
            for(int k=1;k<=m-sum;k++) tran(i,n+1);
            for(int k=1;k<=m-sum;k++) tran(j,i);
            for(int k=1;k<=m;k++)
                if(sta[n+1][top[n+1]]>mid&&top[j]<m) tran(n+1,j);
                else tran(n+1,i);
            tag[j]=true;
        }
    }
    solve(l,mid),solve(mid+1,r);
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) sta[i][++top[i]]=read();
    solve(1,n);
    printf("%d\n",tot);
    for(int i=1;i<=tot;i++) printf("%d %d\n",ans[i][0],ans[i][1]);
    return 0;
}
移球游戏

 

posted @ 2020-12-15 14:29  QDK_Storm  阅读(167)  评论(0编辑  收藏  举报