《卷王》解题报告

目录

  • 前言
  • 题目描述
    • 缩略版
    • 完整版
  • 解题思路
    • 转化
    • 计算
  • 代码

前言

这一次模拟赛折在语文上。

题目描述

概括版

\(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}
};

读者可打开计算器的“十进制转二进制”功能理解。
利用这个表格,就可以实现快速转移状态。

\[dp[i][j]=\mathcal{OR} \{dp[i - 1][j \oplus ef[k][i]]\} \]

代码

#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;
}
posted @ 2025-04-28 15:11  cwkapn  阅读(52)  评论(0)    收藏  举报