cf2232D
发表于|更新于
|浏览量:
链接
https://codeforces.com/contest/2232/problem/D
简要题意
汉诺塔加强版,保留小盘必须在大盘之上的限制,同时移动的规则变更为,盘i能被移动,当且仅当上面正好有$a[i]$个盘(盘i不需要在顶部),问能否让盘在$2^n$步内从A柱移动到C柱,如果可以,构造方案。
大概思路
可以参考普通汉诺塔的做法。
首先有一些必要的限制,$a[i]<i$ 必须成立,否则一定无解。
接下来,我们可以使用归纳法,证明满足任意$a[i]<i$ 时,一定是有解的。
当只有一个盘的时候,可以直接把它从A柱移动到C柱,一个盘的时候有解。
假设现在有$k$个盘,而且他有解。那么对于$k+1$个盘的情况,我们可以先把k-$a[k+1]$个盘移动到B柱,然后把第$k+1$个盘移动到C柱,再把B柱上的盘移回A柱,然后把这k个盘移动到C柱,就移动完成了。
所以可以知道,满足以上条件,一定有解,且构造方式类似于普通汉诺塔。
接下来证明一定可以在$2^n$内解决,同样使用归纳法。
1个盘的时候,操作次数$1<2^1$。
假设n个盘的时候操作次数小于$2^n$,考察$n+1$个盘的操作次数。当$a[n+1]=0$时,退化为普通汉诺塔问题,操作次数为$2^{n+1}-1$;当$a[n+1]!=0$时,操作次数$<=1+2^n-1+2*(2^{n-a[n+1]}-1)<=2^{n+1}-1$
丑陋的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| #include<bits/stdc++.h> using namespace std; #define int long long void solve(){ int n; cin>>n; vector<int> a(n+1); for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<=n;i++){ if(a[i]>=i){ cout<<"NO\n"; return; } } cout<<"YES\n"; vector<array<int,3>> result; auto dfs=[&](auto &&self,int n,int from,int mid,int to)->void{ if(n==0) return; if(n==1){ result.push_back({n,from,to}); return; } if(a[n]==0){ self(self,n-1-a[n],from,to,mid); result.push_back({n,from,to}); self(self,n-1,mid,from,to); }else{ self(self,n-1-a[n],from,to,mid); result.push_back({n,from,to}); self(self,n-1-a[n],mid,to,from); self(self,n-1,from,mid,to); } }; dfs(dfs,n,1,2,3); cout<<result.size()<<"\n"; for(auto &[a,b,c]:result) cout<<a<<" "<<b<<" "<<c<<"\n"; } signed main(){ cin.tie(nullptr)->sync_with_stdio(0); int t=1; cin>>t; while(t--) solve(); }
|