浪潮2020笔试【搬石头,01串】

T1:搬石头

题目描述

沙滩按照线型摆放着n个大小不一的球形石头,已知第i个石头的半径为ri,且不存在两个石头有相同的半径。为了使石头的摆放更加美观,现要求摆放的石头的半径从左往右依次递增。因此,需要对一些石头进行移动,每次操作可以选择一个石头,并把它放在剩下n-1个石头在最左边或最右边。问最少需要操作多少次才能将这n个石头的半径变成升序?

输入描述
第一行一个整数n,表示石头的个数。(1 <= n <= 100000)
第二行n个整数,表示从左往右石头的半径r1,r2,...,rn。(1 <= ri <= n),且保证不存在两个不同的石头拥有相同的半径。 
输出描述
最少操作次数

样例输入
    5
    4 1 2 5 3
样例输出
    2

分析

一开始以为是逆序对,然后又往最长上升子序列那里想,发现n-最长上升子序列正好是答案,而且样例是可以做出来的。但是我自己举了个例子没有做出来。所以我又想了一会儿其他的。

然后神奇的事情发生了:我想先交一个最长上升子序列的代码试试(O(n^2)的DP写法),结果一交,TLE,过了64%。我当时都惊呆了,数据量是10w,TLE那说明答案就是最长上升子序列啊, 只是我的 n^2 写法超时了,然后我就开始写贪心+二分的O(nlogn)写法的最长上升子序列。写完后交上发现Wrong Answer??????难道是我贪心+二分写崩了?然后疯狂的查错,也没发现啥错误。

后来,才推出来正确的答案应该是最长+1连续上升子序列(比如 2 3 4 5这种)。其实也比较好理解,因为如果中间不是+1这种连续的话,到时候还得往外移动。
举个例子: 3 1 2 4 其最长上升子序列是1 2 4,但是这个4是不应该计算的,因为3不可能直接插入到2 4中间,必须先把3放到最后,再把4放到最后这样执行两步。
所以,只要是连续+1的这种子序列,我们都不用移动,而只移动剩下的那些。

答案是n-最长+1连续上升子序列的长度。

代码

怎么求最长+1连续上升子序列?很简单,a[i] 只能由 a[i]-1 转移过来。
所以我们定义f[i] 表示以 i 结尾的最长+1连续上升子序列的长度是 f[i] 。状态转移方程是: f[i]=f[i-1]+1;

#include<iostream>
using namespace std;

const int maxn=10000;
int a[maxn],f[maxn];
 
int main() {
	int n;
	cin>>n;
	for (int i=1; i<=n; i++) //读入 
		cin>>a[i];
		
	for (int i=1; i<=n; i++) //动态规划 
		f[a[i]]=f[a[i]-1]+1;
	
	int ans=-1;
	for (int i=1; i<=n; i++) //统计答案 
		ans=max(ans,f[i]);
	cout<<n-ans<<endl;	
}

 

T2:01串

题目描述

给你一个长度为n的01串。现在想让你找出最长的01交替子串(子串可以不连续)比如:1010,0101是01交替的串,1101则不是。现在你可以把某一个连续的区间进行翻转,即0变1,1变0。问修改之后的最大01交替子串的长度是多少。

输入描述
输入第一行包含一个整数n (1 <= n <= 100000) 表示01串的长度。 第二行包含一个01串。
输出描述
输出一个数表示符合题意的子串长度

样例输入
    8
    10000011
样例输出
    5

分析

首先要明白一个道理:一个串01交替的,如0101...或者1010...这种形式,他翻转之后还是01交替的(只不过换了交替顺序)。

我们先计算原始串的最长01交替串的长度。

我们统计出原始串中有多少个不同的位置,也就是代码中计算的sum。

有sum个不同的位置,最长01交替串是多少?

  • 连续的情况是sum+1个,比如 11101111 ,sum=2,最长01交替串是3个。
  • 不连续的情况是sum+2个,比如 10000001,sum=2,最长01交替串还是3个,因为中间的0只能计算一次。

所以代码11行sum++,表示翻转之前我们能得到的最长01交替串。

要进行翻转了,翻转的目的就是增加01交替串的长度。 之前我们强调过01串翻转之后还是01串,所以原始串的sum是不会丢的。

那么翻转之后会增加多少呢?

  • 连续的情况,我们肯定是把101这个交替串翻转,变为11010111,这样在两边就可以各多增加一个。
  • 不连续的情况,我们把中间的0翻转(最右边的不用翻转),变为10111101,也是两边各增加一个。

所以综上所述,翻转会多增加2个字符的长度。

另外要特判一下sum=n-1的情况,这种情况是输出n(总不能输出sum+2=n+1吧)。

代码

#include<iostream>
using namespace std;
int n,sum;
string s;
int main(){
    cin>>n;
    cin>>s;
    for(int i=0; i<n-1; i++)
       if(s[i]!=s[i+1])
            sum++;
    sum++;
    if(sum<n-1) 
        cout<<sum+2<<endl;
    else
        cout<<n<<endl;
    return 0;
}

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值