題目連接:http://zhengruioi.com/contest/70/problem/270ios
夜色已晚,九條可憐要睡覺了,因而她讓她的管家來把房間裏的燈都關了。測試
可憐的房間裏有 n 盞燈,編號爲 1 到 n,最開始其中一些是亮的,一些是滅的。從第 1 時刻開始,管家每一時刻能夠選擇一盞燈按下開關(固然也能夠選擇不按):若是原來這盞燈是亮的,那麼在按下開關後會熄滅,不然會被點亮。ui
可憐喜歡新奇的小玩意,天然房間裏的燈和普通的燈都不同:若是管家在第 i 時刻按下了第 j 盞燈,那麼對於全部正整數 k∈[1,n−j],在第 i+k時刻,第 j+k 盞燈的開關會被自動按下一次。spa
舉例來講,若是 n=4且管家在前 3 個時刻分別按下了第 1,3,3盞燈的開關,那麼實際上:code
- 第一個時刻,第 1 盞燈的開關被按了一次。
- 第二個時刻,第 2,3 盞燈的開關分別被按了一次。
- 第三個時刻,第 3 盞燈的開關被按了兩次(結果上來講第 3 盞燈的狀態沒有改變),第 4 盞燈的開關被按了一次。
- 第四個時刻,第 4 盞燈的開關被按了兩次。
所以在第 4 個時刻結束的時候,全部燈的狀態都被取反了。blog
由於九條可憐已經很累了,因此只有有一個時刻,全部燈都是滅的,那麼可憐就會立刻睡着(即便在下一時刻又會有燈亮起來)。如今管家想要知道最優狀況下可憐能在第幾時刻入睡。ci
輸入格式
輸入一行一個 01 字符串 s ,其中 |s| 表示燈的個數。第 i 個字符若是是 1 表示初始時這一盞燈是亮着的,不然表示這一盞燈是滅的。字符串
輸出格式
輸出一行一個整數表示答案:可憐最先的入睡時刻。get
樣例1
樣例輸入1
0010
樣例輸出1
1
樣例2
樣例輸入2
0
樣例輸出2
0
樣例3
樣例輸入3
111
樣例輸出3
2
限制與約定
本題採用捆綁測試的方式,有以下 3 個子任務:string
- n≤9 並保證最優解小於等於 9。分值 30 分。
- n≤17。分值 30 分。
- n≤20。分值 40 分。
時間限制:1s
空間限制:512MB
題解:
咱們考慮DP,倒着的那種(倒着指的是時間上的倒着)
考慮最終睡着的時刻k,假如咱們在第k-1的時刻對某盞燈l操做,l到l+1這段區間會被翻轉
那麼咱們繼續倒着往回,最終回到時刻1,總長度爲k也就是咱們求的值
這樣的話咱們就能夠倒着DP了,dp[i][j]表示是否存在狀態,後i個時刻,燈的狀態爲j。當咱們發現存在dp[i][0]說明後i個時刻能夠把燈給關上,那麼時刻總長度就是i,也就是前i個時刻能夠把燈關上
轉移的話就是枚舉翻轉的位置l,將l~l+i-1這一段區間翻轉便可
代碼以下:
#include<algorithm> #include<cstdio> #include<cstring> #include<iostream> using namespace std; string s; int num,n; int dp[21][2000010]; int reverse(int l,int r) { return ((1<<r)-1)-((1<<l-1)-1); } int main() { cin>>s; n=s.size(); for (int i=0;i<n;i++) num+=(s[i]-'0')*(1 << i); if (!num) {puts("0");return 0;} dp[0][num]=1; for (int i=1;i<=n;i++) { for (int s=0;s<(1<<n);s++) { if (!dp[i-1][s]) continue; for (int j=0;j<=n;j++) { if (j==0) dp[i][s]|=dp[i-1][s]; else dp[i][s^(reverse(j,min(j+i-1,n)))]|=dp[i-1][s]; } } if (dp[i][0]) { printf("%d\n",i); return 0; } } return 0; }