[正睿OJ 270] 關燈 解題報告 (倒着DP)

題目連接:http://zhengruioi.com/contest/70/problem/270ios

夜色已晚,九條可憐要睡覺了,因而她讓她的管家來把房間裏的燈都關了。測試

可憐的房間裏有 n 盞燈,編號爲 1 到 n,最開始其中一些是亮的,一些是滅的。從第 1 時刻開始,管家每一時刻能夠選擇一盞燈按下開關(固然也能夠選擇不按):若是原來這盞燈是亮的,那麼在按下開關後會熄滅,不然會被點亮。ui

可憐喜歡新奇的小玩意,天然房間裏的燈和普通的燈都不同:若是管家在第 i 時刻按下了第 j 盞燈,那麼對於全部正整數 k[1,nj],在第 i+k時刻,第 j+k 盞燈的開關會被自動按下一次。spa

舉例來講,若是 n=4且管家在前 3 個時刻分別按下了第 1,3,3盞燈的開關,那麼實際上:code

  1. 第一個時刻,第 1 盞燈的開關被按了一次。
  2. 第二個時刻,第 2,3 盞燈的開關分別被按了一次。
  3. 第三個時刻,第 3 盞燈的開關被按了兩次(結果上來講第 3 盞燈的狀態沒有改變),第 4 盞燈的開關被按了一次。
  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

  1. n9 並保證最優解小於等於 9。分值 30 分。
  2. n17。分值 30 分。
  3. n20。分值 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; }
相關文章
相關標籤/搜索