洛谷 P7792 [COCI2014-2015#7] KRIZA

博客详细介绍了洛谷P7792 [COCI2014-2015#7] KRIZA问题的解题过程,包括暴力解法、优化思路以及正解策略。优化部分提到通过固定钥匙与门之间的差值,将时间复杂度降低到O(k)。正解中利用循环性质求解尝试次数,避免了额外的错误计算。博客最后提醒了两个编程实现时需要注意的细节。
摘要由CSDN通过智能技术生成

前言

更好的阅读体验

前置知识

有手就行

题目描述

题目传送门

题目大意

Sisyphus 在一个圆形的房间里,房间内有 n n n 扇锁着的门,他有 n n n 把钥匙,其中第 i i i 把钥匙对应第 v i v_i vi 扇门,遇到不匹配的钥匙就放到钥匙扣的另一边。他需要解锁所有的门。Sisyphus 沿着门编号顺序,每次到一扇门前就找到第 i i i 把钥匙尝试打开门,直到找到正确的钥匙,然后把门再锁上,一直到他第 k k k 次成功解锁为止。

数据范围

  • 对于 40 % 40\% 40% 的数据,保证 1 l e q n , k ≤ 1000 , 1 ≤ n , k ≤ 1000 1 leq n,k\leq 1000,1 \leq n,k \leq 1000 1leqn,k10001n,k1000
  • 对于 60 % 60\% 60% 的数据,保证 1 1 1 ≤ k ≤ 5 × 1 0 4 , 1 ≤ k ≤ 5 × 1 0 4 \leq k \leq 5 \times 10^4,1 \leq k \leq 5 \times 10^4 k5×1041k5×104
  • 对于所有数据, 1 ≤ v i ≤ n ≤ 1 0 5 , 1 ⩽ v i ≤ n ≤ 1 0 5 , 1 ≤ k ≤ 1 0 9 1\leq v_i\leq n\leq 10^5,1⩽v_i \leq n \leq 10^5,1 \leq k \leq 10^9 1vin1051vin1051k109

思路

初步思路(24pts)

看到此题,第一反应肯定是暴力,模拟到每一扇门尝试每一把钥匙,这样的时间复杂度为 O ( n k ) O(nk) O(nk),肯定过不了,提交只有 24 p t s 24pts 24pts

优化(48pts)

我们需要注意的是,当面对第 i i i 扇门,用最右边的钥匙解锁第 i − 1 i - 1 i1 扇门,并且在用循环变量 i i i j j j 标记的钥匙之间有恒定数量的钥匙(即差一定)。

这样,找出 S i s y p h u s Sisyphus Sisyphus 插错了多少把钥匙的时间复杂度就变成了 O ( 1 ) O(1) O(1),于是我们得到了时间复杂度为 O ( k ) O(k) O(k) 的解,结合数据范围可得,可以拿到 60 % 60\% 60% 的分数。

//48分比较简单,就不加注释了

#include<bits/stdc++.h>
using namespace std;
int v[1000001];
int n,k,ans;
int p=1;
int main()
{
	cin>>n>>k;
	for(int i=1;i<=n;i++)
	{
		int x;
		cin>>x;
		v[x]=i;
		//为了方便处理,把输入的 $v_i$ 改成了表示第 $i$ 扇门的钥匙编号
	}
	for(int i=1;i<=k;i++)
	{
		if(p>n)p=(p-1)%n+1;
		int t=(i-1)%n+1;
		if(v[t]>=p)
		{
			ans+=v[t]-p;
			p+=v[t]-p;
		}
		else
		{
			ans+=v[t]+n-p;
			p+=v[t]+n-p;
		}
	}
	cout<<ans;
	return 0;
}

正解(80pts)

我们不难发现,对于尝试每一圈的 n n n 扇门,其实这是一个循环的过程,所以我们只需要遍历一圈即可。

我们可以用 a i a_i ai 表示从第 1 1 1 扇门到第 i − 1 i-1 i1 扇门尝试的次数,然后我们维护 p p p 表示当前已经选到的钥匙编号,然后从 2 2 2 n + 1 n+1 n+1 遍历,对于每一扇门,更新 p p p a i a_i ai 的值,最后可以计算出试完第 k k k 扇门的尝试次数,即:

a [ n ] × ( k ÷ n ) + a [ k   m o d   n ] a[n]\times(k\div n)+a[k\bmod n] a[n]×(k÷n)+a[kmodn]

这是什么意思?很好理解,我们已经计算出了每圈的尝试次数,我们用次数乘上圈数就是整圈的尝试次数,然后我们再加上剩余不足一圈的尝试次数,就是答案了。

Q:为什么是从 2 2 2 n + 1 n+1 n+1 遍历?

A:因为 a i a_i ai 表示的是从第 1 1 1 扇门到第 i − 1 i-1 i1 扇门尝试的次数。对于第 i − 1 i-1 i1 扇门,从第 1 1 1 扇门到这扇门的尝试次数等于上一次的尝试次数加上这一次的尝试次数。

注意两个坑

  1. 下一扇门的钥匙编号可能到下一圈,更新时要判断。

  2. k k k 一定要减一,因为有 k m o d    n = 0 k\mod n = 0 kmodn=0 的情况,最后可能会多算。

A C AC AC C o d e Code Code

#include<bits/stdc++.h>
using namespace std;
long long v[100001],a[100001];
int n,k;
int p,sum;
long long ans=0;//保险起见,开个long long
int main()
{
	cin>>n>>k;
	k--;
	for(int i=1;i<=n;i++) 
	{
		int x;
		cin>>x;
		v[x]=i;
		//为了方便处理,把输入的 $v_i$ 改成了表示第 $i$ 扇门的钥匙编号
	}
	ans=v[1]-1;//这句很重要:ans的初始值
	p=v[1];//p从1开始
	v[n+1]=v[1];//第n+1扇门就是第1扇门
	for(int i=2;i<=n+1;i++)
	{
		if(v[i]>=p)a[i-1]=a[i-2]+v[i]-p;
		//钥匙的编号和当前的钥匙编号在一圈内:更新a数组:上一扇门加上这扇门需要尝试的次数
		else a[i-1]=a[i-2]+v[i]+n-p;
		//否则在下一圈
		p=v[i];//更新p
	}
	ans+=a[n]*(k/n)+a[k%n];//算答案
	cout<<ans;
	return 0;
}

后记

各位读者,请不要吝啬您的赞和关注哦!

B y    F H e n r y h \Huge\textcolor{gold}{By\ \ FHenryh} By  FHenryh

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值