NOIP2020 T3移球遊戲
阿新 • • 發佈:2020-12-17
題目描述
\(n\)個柱子,每個柱子上面\(m\)個大小相同、顏色不同的球,尋找一種移球方案使得每個柱子上面的球顏色相同。
題解
這題思維難度比較高,程式碼實現難度不大,考場打了一個假掉的做法,看了正解以後感覺非常之妙。
做法挺自然的,首先考慮只有兩種顏色的情況:
- 考慮將所有黑色的球移動到白色的球上方。具體來說:先將要操作的柱子找出來,找到其裡面有多少個黑球(設有\(k\)個),將隨便另外一個柱子最上方的\(k\)個球移到空柱上,然後操作這個柱子,將黑球移到我們選的柱子上,白球移到空柱上,再將黑球和白球先後移會來,因為每時每刻空餘位置數都是\(m\),所以是合法的。
- 然後考慮將每種顏色的球移動到一個柱子上。可以考慮對於每一個要操作的柱子,找到另外一個要操作的柱子,分黑球數之和大於\(m\)
對於\(n>2\)的情況,可以考慮分治,將所有球染為黑白兩色,同顏色的也染為同顏色,然後給這些球各自分配一半的柱子,然後繼續遞迴下去就可以解決了。大概計算一下,總共的移球次數是在\(800000\)左右。
程式碼
#include<cstdio> #include<cstring> #include<algorithm> #define N 60 #define M 500 using namespace std; int n,m,i,j,c[N][M],ans,ans1[1000000][3],num,id[M],cnt[N]; int read(){ int x=0; char ch=getchar(); while (ch>'9'||ch<'0') ch=getchar(); while (ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x; } void move(int x,int y,int num){ int i; for (i=1;i<=num;i++){ cnt[x]--;cnt[y]++; c[y][cnt[y]]=c[x][cnt[x]+1];c[x][cnt[x]+1]=0; ans++; ans1[ans][1]=x;ans1[ans][2]=y; } } void dfs(int l,int r){ int mid=(l+r)/2,i,j,sum,x,y,s1,s2; if (l==r) return; num=0; for (i=1;i<=n;i++) if (c[i][1]>=l&&c[i][2]<=r) id[++num]=i; for (i=1;i<=num;i++){ sum=0; x=id[i];y=id[i%num+1]; for (j=1;j<=m;j++) if (c[x][j]>=l&&c[x][j]<=mid) sum++; if (!sum) continue; move(y,n+1,sum); s1=s2=0; for (j=m;j>=1;j--){ if (s1>=sum) break; if (c[x][j]>=l&&c[x][j]<=mid){ s1++; move(x,y,1); } else{ s2++; move(x,n+1,1); } } move(n+1,x,s2); move(y,x,s1); move(n+1,y,s1); } for (i=1;i<=num-1;i++){ x=id[i];y=id[i%num+1]; s1=s2=0; for (j=1;j<=m;j++){ if (c[x][j]>=l&&c[x][j]<=mid) s1++; if (c[y][j]>=l&&c[y][j]<=mid) s2++; } if (s1==m||s1==0) continue; if (s1+s2>m){ move(x,n+1,m); move(y,x,s2); move(n+1,y,m-s1); move(n+1,x,m-s2); move(n+1,y,cnt[n+1]); } else{ move(x,n+1,s1); move(y,n+1,s2); move(y,x,s1); move(n+1,y,s1+s2); } } dfs(l,mid);dfs(mid+1,r); } int main(){ freopen("ball.in","r",stdin); freopen("ball.out","w",stdout); n=read();m=read(); for (i=1;i<=n;i++){ cnt[i]=m; for (j=1;j<=m;j++) c[i][j]=read(); } dfs(1,n); printf("%d\n",ans); for (i=1;i<=ans;i++) printf("%d %d\n",ans1[i][1],ans1[i][2]); return 0; }