题目链接:http://zhengruioi.com/contest/70/problem/270
夜色已晚,九条可怜要睡觉了,于是她让她的管家来把房间里的灯都关了。
可怜的房间里有 n 盏灯,编号为 1 到 n,最开始其中一些是亮的,一些是灭的。从第 1 时刻开始,管家每一时刻可以选择一盏灯按下开关(当然也可以选择不按):如果原来这盏灯是亮的,那么在按下开关后会熄灭,否则会被点亮。
可怜喜欢新奇的小玩意,自然房间里的灯和普通的灯都不一样:如果管家在第 i 时刻按下了第 j 盏灯,那么对于所有正整数 k∈[1,n−j],在第 i+k时刻,第 j+k 盏灯的开关会被自动按下一次。
举例来说,如果 n=4且管家在前 3 个时刻分别按下了第 1,3,3盏灯的开关,那么实际上:
- 第一个时刻,第 1 盏灯的开关被按了一次。
- 第二个时刻,第 2,3 盏灯的开关分别被按了一次。
- 第三个时刻,第 3 盏灯的开关被按了两次(结果上来说第 3 盏灯的状态没有改变),第 4 盏灯的开关被按了一次。
- 第四个时刻,第 4 盏灯的开关被按了两次。
因此在第 4 个时刻结束的时候,所有灯的状态都被取反了。
因为九条可怜已经很累了,所以只有有一个时刻,所有灯都是灭的,那么可怜就会马上睡着(即使在下一时刻又会有灯亮起来)。现在管家想要知道最优情况下可怜能在第几时刻入睡。
输入格式
输入一行一个 01 字符串 s ,其中 |s| 表示灯的个数。第 i 个字符如果是 1 表示初始时这一盏灯是亮着的,否则表示这一盏灯是灭的。
输出格式
输出一行一个整数表示答案:可怜最早的入睡时刻。
样例1
样例输入1
0010
样例输出1
1
样例2
样例输入2
0
样例输出2
0
样例3
样例输入3
111
样例输出3
2
限制与约定
本题采用捆绑测试的方式,有如下 3 个子任务:
- 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; }