The Red Button
問題
問題描述
Piegirl終於發現了紅色按鈕,你現在還剩最後一個機會去改變這個結局。這個按鈕下面的電路由n個從0到n-1編號節點組成。為了關閉這個按鈕,這n個節點必須以特定的序列拆解。節點0必須首先拆解,在拆解了節點i後,下一個被拆解的節點必須是(2·i) mod n或(2·i)+1 mod n。最後一個被拆解的節點必須是節點0。節點0必須被拆解兩次,其他節點必須剛好被拆解一次。你的任務是找到一個符合要求的順序並輸出它。如果沒有任何一個順序滿足條件,輸出-1。
輸入格式
包含一個整數n(2<=n<=105)
輸出格式
輸出一個可以拆解所有節點的順序。如果不可能輸出-1。如果有多個可能的順序,輸出任意一個。
樣例輸入
資料1
2
資料2
3
資料3
4
資料4
16
2
資料2
3
資料3
4
資料4
16
樣例輸出
資料1
0 1 0
資料2
-1
資料3
0 1 3 2 0
資料4
0 1 2 4 9 3 6 13 10 5 11 7 15 14 12 8 0
0 1 0
資料2
-1
資料3
0 1 3 2 0
資料4
0 1 2 4 9 3 6 13 10 5 11 7 15 14 12 8 0
資料規模和約定
對於15%的資料2<=n<=10
對於30%的資料2<=n<=20
對於100%的資料2<=n<=105
對於30%的資料2<=n<=20
對於100%的資料2<=n<=105
解法
一開始的思路是DFS,每個節點最多有兩個方向,可以就走,不能就回溯找另一個方向,這樣數量大之後就會TLE,自測120多就出不來結果
TLE程式碼:
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+10; int n; int len; int dist[maxn]; bool vis[maxn]; bool dfs(int k,int d) { if(d==n-1&&(k*2==n||k*2+1==n)) { dist[d]=k; dist[n]=0; return true; } dist[d]=k; // cout<<d<<" :"<<k<<endl; int ne=(k*2)%n; if(vis[ne]==false) { vis[ne]=true; if(dfs(ne,d+1)) return true; vis[ne]=false; } int nex=(k*2+1)%n; if(vis[nex]==false) { vis[nex]=true; if(dfs(nex,d+1)) return true; vis[nex]=false; } return false; } int main() { int i,j; cin>>n; vis[0]=true; if(n&1) cout<<"-1"<<endl; else { if(dfs(0,0)) { for(i=0;i<=n;i++) { if(i!=0) cout<<" "; cout<<dist[i]; } } } return 0; }
正確解法:
只需標記所有節點一遍即可,第一個走頭無路的點就是終點,第二個走投無路的點是倒數第二個終點。。。。
因此,只需標記完所有節點一次,就可得出結果的倒敘。反序後再加上0,就為最終答案。對於偶數直接輸出-1
正確程式碼:
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+10; int n; vector<int> dist; bool vis[maxn]; void dfs(int k) { vis[k]=true; if(!vis[(k*2)%n]) dfs((k*2)%n); if(!vis[(k*2+1)%n]) dfs((k*2+1)%n); dist.push_back(k); } int main() { int i,j; cin>>n; vis[0]=true; if(n&1) cout<<"-1"<<endl; else { dfs(0); reverse(dist.begin(),dist.end()); dist.push_back(0); for(i=0;i<dist.size();i++) cout<<dist[i]<<" "; cout<<endl; } return 0; }