1. 程式人生 > >太鼓達人 C組模擬賽

太鼓達人 C組模擬賽

題目大意:

是M個圍成一圈的感測器。每個感測器都有開和關兩種工作狀態,分別用1和0表示。顯然,從不同的位置出發沿順時針方向連續檢查K個感測器可以得到M個長度為K的01串。Vani知道這M個01串應該是互不相同的。M會取到可能的最大值。現在Vani已經瞭解到了K的值,他希望你求出M的值,並給出字典序最小的感測器排布方案。

解題思路:

很顯然,第一問的答案就是 2^n。
爆搜直接0ms 236KB過
第二問,構造一個有 2^(n-1)個節點的圖,對應 2^(n-1)個 n-1 位二進位制數。從代表數 k 的節
點(0<=k<2^(n-1))向代表數(k<<1)&(1<<(n-1))的節點,和代表數((k<<1)+1)&(1<<(n-1))的節點
分別連一條邊。可以發現這樣的圖中,所有點的入度和出度都是 2,因此這是一個尤拉圖。
因此我們從 0 號點開始 dfs 尋找一個歐拉回路,回溯的時候記錄到棧中,最後倒序輸出即可。
因為要求字典序最小,dfs 的時候要注意搜尋順序,先走 0 邊,再走 1 邊。這個演算法尋找歐
拉回路,每個點、每條邊只訪問一次,是 O(V+E)級別的。
時間複雜度 O(2^n),預計得分 100 分。

源程式:

#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cstring>
#define gc getchar()
using namespace std;
const int N=1<<12;
const int INF=~0U>>1;
int n,k,T,ans[N],p[N]; 
void read(int &f)
{
    int d=0;f=1;char c=gc;
    while(!isdigit(c)){if
(c=='-') f=-1;c=gc;} while(isdigit(c)) {d=d*10+(int)c-48;c=gc;} f*=d;return; } void write(int x) {if (x>9) write(x/10);putchar(x%10+48);return;} bool dfs(int x,int now,int q) { if (x==INF) return 1; if (p[now]) return 0; int nex=now; nex-=(nex>=T)*T; nex=nex<<1; p[now]=1
; ans[x]=q; int to=x+1; to=to>k?1:to; to=to==n?INF:to; if (dfs(to,nex|0,0)) return 1; if (dfs(to,nex|1,1)) return 1; p[now]=0; return 0; } int main() { read(n);k=(1<<n);T=1<<(n-1); dfs(n,0,0); write(k);putchar(32); for (int i=1;i<=k;i++) write(ans[i]); return 0; }