《卷王》解题报告
目录
- 前言
- 题目描述
- 缩略版
- 完整版
- 解题思路
- 转化
- 计算
- 代码
前言
这一次模拟赛折在语文上。
题目描述
概括版
有 \(n\) 个开关,存在初始状态(开或关)。你第 \(x\) 秒内可以拨动不超过一次开关。当第 \(i\) 个和 \(i + p(p>0)\) 个开关存在时,若第 \(i\) 个开关状态发生改变,则在 \(p\) 秒后第 \(i + p\) 个开关因第 \(i\) 个开关联动(不算在你拨动的次数里)。若第 \(i\) 个开关在第 \(j\) 秒内被拨动 \(2\) 次,则其开关状态不变。求使所有开关状态均为关的最小操作次数,或报告无解。
完整版
夜深了,作为全宿舍卷到最晚的人,卷王之 y 准备关灯睡觉了。
y 前面有 \(n\) 个灯,每个灯前面有个开关。每一秒的开始,y 可以按下一个开关或者不按。当按下第 \(i\) 个开关后,如果这一秒第 \(i\) 个灯是亮的就会变暗,如果灯是暗的就会变亮。
这一排灯有一个奇怪的特性:在你按下一个开关后,其他灯也会变。具体的,当前这一秒第 \(i\) 个灯会反转,下一秒末第 \(i+1\) 个灯会反转 以此类推直到第 \(n\) 个灯反转。只有所有灯都是灭的的时候才能关闭这个特性。之后不按开关的情况下灯就会一直灭。
y 想抓紧时间睡觉好起来继续卷,求 y 最少需要的时间。
解题思路
转化
假设答案为 \(t\),则第 \(1\) 秒按的开关接下来 \(t\) 秒都会有作用,第 \(2\) 秒按的开关接下来 \(t - 1\) 秒都会有作用,第 \(3\) 秒按的开关接下来 \(t - 2\) 秒都会有作用,……,第 \(t\) 秒按的开关只在最后 \(1\) 秒有作用。注意到,按时间从后向前推导是没有后效性的。
那么考虑从后往前进行动态规划。
计算
设 \(dp[i][S]=0/1\) 表示在 \(i\) 秒内按下的开关使状态 \(S\) 变为全 \(0\)。考虑设 \(ef[i][j]\) 的二进制表示为以低 \(i\) 位为结束的连续 \(j\) 个 \(1\),则有:
int ef[N][N] = {
{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{0, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
{0, 4, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7},
{0, 8, 12, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15},
{0, 16, 24, 28, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31},
{0, 32, 48, 56, 60, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63},
{0, 64, 96, 112, 120, 124, 126, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127},
{0, 128, 192, 224, 240, 248, 252, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
{0, 256, 384, 448, 480, 496, 504, 508, 510, 511, 511, 511, 511, 511, 511, 511, 511, 511},
{0, 512, 768, 896, 960, 992, 1008, 1016, 1020, 1022, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023},
{0, 1024, 1536, 1792, 1920, 1984, 2016, 2032, 2040, 2044, 2046, 2047, 2047, 2047, 2047, 2047, 2047, 2047},
{0, 2048, 3072, 3584, 3840, 3968, 4032, 4064, 4080, 4088, 4092, 4094, 4095, 4095, 4095, 4095, 4095, 4095},
{0, 4096, 6144, 7168, 7680, 7936, 8064, 8128, 8160, 8176, 8184, 8188, 8190, 8191, 8191, 8191, 8191, 8191},
{0, 8192, 12288, 14336, 15360, 15872, 16128, 16256, 16320, 16352, 16368, 16376, 16380, 16382, 16383, 16383, 16383, 16383},
{0, 16384, 24576, 28672, 30720, 31744, 32256, 32512, 32640, 32704, 32736, 32752, 32760, 32764, 32766, 32767, 32767, 32767},
{0, 32768, 49152, 57344, 61440, 63488, 64512, 65024, 65280, 65408, 65472, 65504, 65520, 65528, 65532, 65534, 65535, 65535},
{0, 65536, 98304, 114688, 122880, 126976, 129024, 130048, 130560, 130816, 130944, 131008, 131040, 131056, 131064, 131068, 131070, 131071},
{0, 131072, 196608, 229376, 245760, 253952, 258048, 260096, 261120, 261632, 261888, 262016, 262080, 262112, 262128, 262136, 262140, 262142}
};
读者可打开计算器的“十进制转二进制”功能理解。
利用这个表格,就可以实现快速转移状态。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 18;
int t, n;
string s;
int p[N] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072};
int ef[N][N] = {
{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{0, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
{0, 4, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7},
{0, 8, 12, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15},
{0, 16, 24, 28, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31},
{0, 32, 48, 56, 60, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63},
{0, 64, 96, 112, 120, 124, 126, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127},
{0, 128, 192, 224, 240, 248, 252, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
{0, 256, 384, 448, 480, 496, 504, 508, 510, 511, 511, 511, 511, 511, 511, 511, 511, 511},
{0, 512, 768, 896, 960, 992, 1008, 1016, 1020, 1022, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023},
{0, 1024, 1536, 1792, 1920, 1984, 2016, 2032, 2040, 2044, 2046, 2047, 2047, 2047, 2047, 2047, 2047, 2047},
{0, 2048, 3072, 3584, 3840, 3968, 4032, 4064, 4080, 4088, 4092, 4094, 4095, 4095, 4095, 4095, 4095, 4095},
{0, 4096, 6144, 7168, 7680, 7936, 8064, 8128, 8160, 8176, 8184, 8188, 8190, 8191, 8191, 8191, 8191, 8191},
{0, 8192, 12288, 14336, 15360, 15872, 16128, 16256, 16320, 16352, 16368, 16376, 16380, 16382, 16383, 16383, 16383, 16383},
{0, 16384, 24576, 28672, 30720, 31744, 32256, 32512, 32640, 32704, 32736, 32752, 32760, 32764, 32766, 32767, 32767, 32767},
{0, 32768, 49152, 57344, 61440, 63488, 64512, 65024, 65280, 65408, 65472, 65504, 65520, 65528, 65532, 65534, 65535, 65535},
{0, 65536, 98304, 114688, 122880, 126976, 129024, 130048, 130560, 130816, 130944, 131008, 131040, 131056, 131064, 131068, 131070, 131071},
{0, 131072, 196608, 229376, 245760, 253952, 258048, 260096, 261120, 261632, 261888, 262016, 262080, 262112, 262128, 262136, 262140, 262142}
};
int ans[1 << N];
bool dp[N][1 << N];
void init() {
for (int i = 1; i < 65536; i++) ans[i] = N;
ans[0] = 0;
dp[0][0] = true;
for (int i = 1; i <= 16; i++) {
for (int j = 0; j < p[16]; j++) {
for (int k = 0; k <= 16; k++) {
dp[i][j] |= dp[i - 1][j ^ ef[k][i]];
}
dp[i][j] |= dp[i - 1][j];
if (dp[i][j]) ans[j] = min(ans[j], i);
}
}
return;
}
int main() {
#ifndef CWKAPN
freopen("roll.in", "r", stdin);
freopen("roll.out", "w", stdout);
#endif
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
init();
cin >> t;
while (t--) {
cin >> s;
n = s.size();
int cnt = 0;
for (int i = 0; i < n; i++) cnt += p[n - i - 1] * (s[i] - '0');
cout << ans[cnt] << '\n';
}
return 0;
}
本文来自博客园,作者:cwkapn,转载请注明原文链接:https://www.cnblogs.com/cwkapn/p/18849563


浙公网安备 33010602011771号