题目连接: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; }