SDNU_ACM_ICPC_2022_Weekly_Practice_1rd「个人赛」J题题解

题目描述:

J - Manga

Takahashi is going to read a manga series "Snuke-kun" in 10^9109 volumes.
Initially, Takahashi has NN books of this series. The ii-th book is Volume a_iai​.

Takahashi may repeat the following operation any number of (possibly zero) times only before he begins to read:

  • Do nothing if he has 11 or less books; otherwise, sell two of the books he has and buy one book of any volume instead.

Then, Takahashi reads Volume 11, Volume 22, Volume 33, \ldots…, in order. However, when he does not have a book of the next volume to read, he stops reading the series (regardless of the other volumes he has).

Find the latest volume of the series that he can read up to. If he cannot read any, let the answer be 00.

  • 1≤N≤3×105
  • 1 \leq a_i \leq 10^91≤ai​≤109
  • All values in the input are integers.

 简而言之就是给n个数字,可以通过删除两个数字换一个指定的数字,最后能得到连续的1~ans中ans的最大值就是要输出的结果。

思路:

首先是将已经有的数字做好标记,因为总共只有n个数字,所以ans的值是一定不会超过n的,我们在输入数据的时候可以默认大于n的数字是要给拿去换的。

cin>>n;
for(i=1;i<=n;i++)
{
	cin>>a;
	if(a<=n) b[a]=1;
}

之后用num=n将n赋值的num,作为手头拥有的数字,用两个数字换一个数字的时候我们不能用需要的组成1~ans序列中的数字去换,所以直接去减num的方式来换掉这些数字。

这里做个比喻:我要在200元里留50元吃疯狂星期四,手里的200由各种各样的纸币组成,与其具体研究哪几张纸币不能动留着吃疯狂星期四,不如花掉150元把剩下的钱作为吃疯狂星期四的钱,那50元是由原本的钱中的哪几张完全无所谓。

num=n;
ans=0;
for(i=1;i<=n;i++)
{
	if(!num) break;//如果没有数了,相当于要留下来的钱以外的钱花光了,退出循环
	if(b[i]) num--;
    else 
	{
		if(num>=2) num=num-2;
		else break;//剩下来不到2个数不足以弥补缺失的数
	}
	ans=i;//每次循环结束记录走到的数
}

 遍历1到n,如果遇到了已经有数,执行num--相当于把那个数放入保护区,接下来删除是不会删那些数的。遇到没有的数就试着用两个数去换缺的数。

完整代码:

#include<cstdio>
#include<iostream>
using namespace std;
int a,num,ans,n,i;
int b[300050];
int main()
{
	cin>>n;
	for(i=1;i<=n;i++)
	{
		cin>>a;
		if(a<=n) b[a]=1;
	}
	num=n;
	ans=0;
	for(i=1;i<=n;i++)
	{
		if(!num) break;
		if(b[i]) num--;
		else 
		{
			if(num>=2) num=num-2;
			else break;
		}
		ans=i;
	}
	cout<<ans<<endl;
	return 0;
}

总结:

没有要求输出具体的操作过程的题目可以将需要操作的元素与不需要操作的元素等价思考

反思(鼠鼠特有的考虑不周呢):

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int a[300002],k,last,num,n,i;
int main()
{
	cin>>n;
	for(i=1;i<=n;i++)//遍历数据讲数据储存在a[i]中
	{
		cin>>a[i];
	}
	sort(a+1,a+n+1);//将数组升序排列
	i=1;
	last=1;//下一个需要的数字的值
	num=n;
	k=0;//储存钱,一个数字换一个钱,两个钱换一个新数字
	while(i<=num||k>=2)//如果
	{
		if(a[i]==last)//如果这个数字刚好是需要的值,直接读取下一个数字
		{
			i++;
			last++;
		}
		else if(a[i]<last)//升序数列中,如果这个数字小于需要的数字就是重复已经有的数字
		{
			i++;
			k++;
		}
		else if(a[i]>last)//大于需要的数字,说明中间缺了数
		{
			if(k>=2)//钱可以换数字时换数字
			{
				k=k-2;
				last++;
			}
			else //不可以换数字时从后面删数字换
			{
				while(k<2)
				{
					num=num-1;
					k++;
				}
				k-=2;
				if(i>num+1) break;
				last++;
			}
		}
	}
	cout<<last-1;
	return 0;
}

这个思路有两个致命缺陷

一、具体地去考虑要哪个数,重复的是哪个数,删掉的是哪个数,机械的模拟使程序计算步骤冗杂。

二、并不是每次都是从后面开始删为最优解。

我在解决这道问题时参考了SDNU_ACM_ICPC_2022_Weekly_Practice_1rd「个人赛」题解的思路并向作者提出了自己的错误思路

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值