[正睿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; }
相关文章
相关标签/搜索