[USACO23JAN] Lights Off G题解

文章讲述了如何解决USACO比赛中的LightsOffG问题,涉及最小操作次数以关闭所有灯的策略。通过处理01字符串,结合开关状态和灯的状态,使用位运算和动态规划来优化操作次数,最终达到时间复杂度为O(n*2^n+nT)的解决方案。

洛谷[USACO23JAN] Lights Off G

题目大意

贝西想要睡觉,但是农场的灯一直开着,影响它睡觉。 贝西有两个长度为 n n n 01 01 01字符串,分别表示 n n n盏灯的序列和开关的序列。其中,表示灯的序列, 0 0 0表示关灯, 1 1 1表示开灯;表示开关的序列, 1 1 1表示可操控的, 0 0 0表示不可操控。

一次操作由下面几个步骤组成:

  • 将一个开关的状态变换,即由可操控变成不可操控;或者不可操控变成可操控
  • 对每一个可操控的开关,按下开关,对应的灯的状态将发生变换,开变成关,关变成开
  • 将开关循环移位,即开始的开关是 s 0 s 1 … s n − 1 s_0s_1\dots s_{n-1} s0s1sn1,循环移位后变成 s n − 1 s 0 … s n − 2 s_{n-1}s_0\dots s_{n-2} sn1s0sn2

求最小的操作次数,使得所有的灯都关掉。

t t t组数据。

1 ≤ t ≤ 2 × 1 0 5 , 2 ≤ n ≤ 20 1\leq t\leq 2\times 10^5,2\leq n\leq 20 1t2×105,2n20


题解

设灯的 01 01 01串为 a a a,开关的 01 01 01串为 b b b

首先,操作次数不会超过 3 n 3n 3n。先用最多 n n n次来将所有开关全部关闭,然后每连续两次,分别打开、关闭开关来把某个灯关掉。

因为操作一是需要设定的,而操作二和操作三是一定的,所以可以将两者分开处理。

对于操作二和操作三,就是 a = a ⊕ b a=a\oplus b a=ab,然后将 b b b循环移位,再进行 a = a ⊕ b a=a\oplus b a=ab,进行的次数与操作次数一致。

对于操作一,设 f i , j f_{i,j} fi,j表示前 i i i次是否能达到状态 j j j,那么 f i , j f_{i,j} fi,j可以由 f i − 1 , j ⊕ x f_{i-1,j\oplus x} fi1,jx转移得到,其中 x x x可取所有长度为 i i i的连续段。

这样做的话,总时间复杂度为 O ( n 2 ⋅ 2 n + n T ) O(n^2\cdot 2^n+nT) O(n22n+nT)。但因为常数较大,所以我们还需要进一步优化。

如果两个状态 x , y x,y x,y可以用过循环移动得到,那么显然 f i , x = f i , y f_{i,x}=f_{i,y} fi,x=fi,y,所以可以用最小的 j j j作为所有能通过循环移动得到 j j j的元素的代表 v j v_j vj。这样的话,能通过循环移动得到的 j j j只需计算一次。那么,固定 x x x移动 j j j,即可处理 j j j经过各个 x x x来更新 f i , j f_{i,j} fi,j的情况。

总时间复杂度为 O ( n ⋅ 2 n + n T ) O(n\cdot 2^n+nT) O(n2n+nT)

code

#include<bits/stdc++.h>
using namespace std;
int qt,n,v1,v2,v[1<<21];
bool f[65][1<<21];
char s[105],t[105];
int tn(int x){
	return (x>>1)|((x&1)<<n-1);
}
int gt(int x,int y){
	if(x==0) return 0;
	for(int i=1;i<=3*n;i++,y=tn(y)){
		x^=y;
		if(f[i][v[x]]) return i;
	}
}
int main()
{
	scanf("%d%d",&qt,&n);
	memset(v,-1,sizeof(v));
	for(int i=0;i<1<<n;i++){
		int p=i;
		while(v[p]==-1){
			v[p]=i;p=tn(p);
		}
	}
	f[0][0]=1;
	for(int i=1,x=0;i<=3*n;i++){
		x^=1<<(i-1)%n;
		for(int j=0;j<1<<n;j++){
			f[i][v[j]]|=f[i-1][v[j^x]];
		}
	}
	while(qt--){
		scanf("%s%s",s+1,t+1);
		v1=v2=0;
		for(int i=1;i<=n;i++){
			v1=v1<<1|(s[i]-'0');
			v2=v2<<1|(t[i]-'0');
		}
		printf("%d\n",gt(v1,v2));
	}
	return 0;
}
源码地址: https://pan.quark.cn/s/d1f41682e390 miyoubiAuto 米游社每日米游币自动化Python脚本(务必使用Python3) 8更新:更换cookie的获取地址 注意:禁止在B站、贴吧、或各大论坛大肆传播! 作者已退游,项目不维护了。 如果有能力的可以pr修复。 小引一波 推荐关注几个非常可爱有趣的女孩! 欢迎B站搜索: @嘉然今天吃什么 @向晚大魔王 @乃琳Queen @贝拉kira 第三方库 食用方法 下载源码 在Global.py中设置米游社Cookie 运行myb.py 本地第一次运行时会自动生产一个文件储存cookie,请勿删除 当前仅支持单个账号! 获取Cookie方法 浏览器无痕模式打开 http://user.mihoyo.com/ ,登录账号 按,打开,找到并点击 按刷新页面,按下图复制 Cookie: How to get mys cookie 当触发时,可尝试按关闭,然后再次刷新页面,最后复制 Cookie。 也可以使用另一种方法: 复制代码 浏览器无痕模式打开 http://user.mihoyo.com/ ,登录账号 按,打开,找到并点击 控制台粘贴代码并运行,获得类似的输出信息 部分即为所需复制的 Cookie,点击确定复制 部署方法--腾讯云函数版(推荐! ) 下载项目源码和压缩包 进入项目文件夹打开命令行执行以下命令 xxxxxxx为通过上面方式或取得米游社cookie 一定要用双引号包裹!! 例如: png 复制返回内容(包括括号) 例如: QQ截图20210505031552.png 登录腾讯云函数官网 选择函数服务-新建-自定义创建 函数名称随意-地区随意-运行环境Python3....
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值