2410. Swapity Swap

题目描述了Farmer John的奶牛晨练方案,通过一系列位置的反转操作改变奶牛的顺序。给定奶牛数量N、反转操作对数M和重复次数K,求K次操作后的奶牛顺序。输入包括N, M, K以及M对反转区间,输出每个位置上的奶牛编号。" 113069531,10535817,Vue与ElementUI动态表单及子表单构建,"['前端开发', 'Vue框架', '表单组件', 'ElementUI']

2410. Swapity Swap

题目描述
Farmer John 的 N 头奶牛(1≤N≤10^5)站成一排。对于每一个 1≤i≤N,从左往右数第 i 头奶牛的编号为 i。Farmer John 想到了一个新的奶牛晨练方案。他给奶牛们 M 对整数 (L1,R1)…(LM,RM),其中 1≤M≤100。他让她们重复以下包含 M 个步骤的过程 K(1≤K≤10^9)次:
对于从 1 到 M 的每一个步骤i:当前从左往右数在位置 Li…Ri 的奶牛序列反转她们的顺序。
当奶牛们重复这一过程 K 次后,请对每一个 1≤i≤N 输出从左往右数第 i 头奶牛的编号。

输入
输入的第一行包含 N, M 和 K。对于每一个 1≤i≤M,第 i+1 行包含 Li 和 Ri,均为范围在 1…N 内的整数,其中 Li<Ri。

输出
在第 i 行输出指令序列执行了 K 次后奶牛序列中从左往右数第 i 个元素的编号。

样例输入

7 2 2
2 5
3 7

样例输出

1
2
4
3
5
7
6

数据范围限制
测试点 1-2 满足 N=K=100。
测试点 3-5 满足 K≤10^3。
测试点 6-10 没有额外限制。

提示
初始时,奶牛们的顺序从左往右为 [1,2,3,4,5,6,7]。在这一过程的第一步过后,顺序变为 [1,5,4,3,2,6,7]。在这一过程的第二步过后,顺序变为 [1,5,7,6,2,3,4]。再重复这两个步骤各一次可以得到样例的输出。

方法一:
暴力,30%。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout);
using namespace std;
const int MAX=2147483647;
const int N=1e5+5;
int f[N],l[110],r[110],n,m,k;
int main()
{
	//fre(swap);
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=n;i++) f[i]=i;
	for(int i=1;i<=m;i++) scanf("%d%d",&l[i],&r[i]);
	while(k--)
		for(int i=1;i<=m;i++) 
			for(int j=l[i];j<=(l[i]+r[i]-1)/2;j++) swap(f[j],f[l[i]+r[i]-j]); 		
	for(int i=1;i<=n;i++)	printf("%d\n",f[i]);
	return 0;
}

方法二(快速幂优化):

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout);
using namespace std;
const int MAX=2147483647;
const int N=100010;
int a[N],b[N],f[N],n,m,k;
int main()
{
	//fre(swap);
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=n;i++) a[i]=i;
	for(int i=1;i<=m;i++)
	{
		int l,r;
		scanf("%d%d",&l,&r);
		for(int j=l;j<=(l+r-1)/2;j++) swap(a[j],a[l+r-j]);
	}
	for(int i=1;i<=n;i++) f[i]=a[i],a[i]=i;
	while(k>0)
	{
		if(k&1)
			for(int i=1;i<=n;i++) a[i]=f[a[i]];
		for(int i=1;i<=n;i++) b[i]=f[f[i]];
		for(int i=1;i<=n;i++) f[i]=b[i];
		k/=2;
	}
	for(int i=1;i<=n;i++) printf("%d\n",a[i]);
	return 0;
}

### 问题解析 USACO 2020年2月比赛中,题目“Swapity Swap”要求解决一个关于数组变换的问题。题目描述如下: Farmer John 有 $ N $ 头奶牛,按顺序编号 $ 1 $ 到 $ N $。他让奶牛按照以下操作进行 $ M $ 轮变换: 1. **第一步**:选择前 $ K $ 头奶牛,将它们反转。 2. **第二步**:选择后 $ K $ 头奶牛,将它们反转。 目标是确定经过 $ M $ 轮变换后,每头奶牛的最终位置。 ### 解题思路 此问题可以通过模拟变换来解决,但直接模拟 $ M $ 次变换可能效率较低,特别是当 $ N $ 和 $ M $ 都很大时。因此,需要优化算法。 #### 1. 发现周期性 由于每一轮变换是固定的,数组的状态会在若干轮后重复。通过记录每一轮变换后数组的状态,可以找到一个循环周期。一旦找到周期,只需要计算 $ M $ 对周期取模,从而减少计算量。 #### 2. 模拟与优化 对于每一轮变换,可以使用数组操作来实现反转逻辑。具体步骤如下: - 对数组的前 $ K $ 个元素进行反转。 - 对数组的后 $ K $ 个元素进行反转。 通过记录每一轮变换后的状态,可以快速找到循环周期。 #### 3. 代码实现 以下是一个高效的实现方法,结合了周期性优化: ```python def swapity_swap(N, K, M): # 初始数组 arr = list(range(1, N + 1)) # 用于记录每一轮变换后的状态 seen = {} for i in range(M): # 将状态转换为元组以便存储 state = tuple(arr) # 如果状态已出现,找到周期 if state in seen: remaining = M - i cycle_length = i - seen[state] # 只需要再执行 remaining % cycle_length 次即可 for _ in range(remaining % cycle_length): transform(arr, K, N) return # 记录当前状态 seen[state] = i # 执行变换 transform(arr, K, N) # 如果没有循环,直接返回当前状态 def transform(arr, K, N): # 前K个元素反转 arr[:K] = arr[:K][::-1] # 后K个元素反转 arr[-K:] = arr[-K:][::-1] ``` ### 示例说明 对于 $ N = 7 $, $ K = 3 $, $ M = 2 $ 的情况: - 初始数组为 `[1, 2, 3, 4, 5, 6, 7]` - 第一轮变换后变为 `[3, 2, 1, 4, 6, 5, 4]` - 第二轮变换后变为 `[1, 2, 3, 4, 5, 6, 7]` 可以发现,经过两轮变换后数组恢复原状,表明周期为 2。 ### 复杂度分析 - **时间复杂度**:最坏情况下为 $ O(M \times K) $,但由于周期性优化,实际运行时间显著减少。 - **空间复杂度**:需要额外空间存储状态,最坏情况下为 $ O(N \times M) $。 ### 适用范围 该方法适用于 $ N $、$ K $ 和 $ M $ 都较大的情况,尤其适合需要处理周期性问题的场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值