1. 程式人生 > 實用技巧 >LG P7115 移球遊戲

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; }
移球遊戲