题目链接https://www.acwing.com/problem/content/4370/
方法一 —— 反向比较(双指针)
分析
从后往前比较
想要求最小操作次数,那么每个数最多只能移动一次,因为一个数如果移动了两次,肯定也会影响其他的数,导致重复操作,竟然如此,我们就来求哪几个数需要移动就好了!需要移动的数的个数就是我们的最小操作数。
示例数据1:
一开始我们的指针都指向最后一个元素
5 4 3 2 1
👆
1 2 3 4 5
👆
我们移动a中指针找到与b指针相对应的奶牛
5 4 3 2 1
👆
1 2 3 4 5
👆
此时可以看出,在这头奶牛后一共有四头奶牛需要移动,所以答案就是 4
示例数据2:
5 1 3 2 4
👆
4 5 2 1 3
👆
可以看出,2、4是在奶牛3前面的,所以需要移动两次,同时标记这两个元素
5 1 3 2 4
👆
4 5 2 1 3
👆
两个指针都移动一次,发现所指奶牛一样,表示1、3奶牛之间不需要移动
5 1 3 2 4
👆
4 5 2 1 3
👆
此时b指针指向2,a指针指向5,但2已经被标记过,所以2和5之间的奶牛不需要移动
5 1 3 2 4
👆
4 5 2 1 3
👆
此时两指针指向的奶牛相同,不进行操作
所以最终的答案为 2
由示例可知:
当两个指针指向的牛不相同时,a指针向前移动,并对前面的奶牛进行标记,直至和b指针指向的奶牛一样,此时a,b指针都往前移动
如果b指针指向的牛没被标记过,继续比较,被标记过则b指针往前移动,直至指向没被标记的奶牛
代码:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100010;
int a[N];
int b[N];
bool st[N]; //标记奶牛是否移动过
int n;
//双指针算法 -- 反向思维
//
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
int res=0;
for(int i=n,j=n;i&&j;i--,j--)
{
//如果当前b指针指向的奶牛被标记过,直接跳过它,去找下一头牛
while(st[b[j]]) j--;
//当两头奶牛的标号相同且没有被标记过时,可以进行操作了
while(a[i]!=b[j])
{
st[a[i]]=1;
res++;
i--;
}
}
cout<<res<<endl;
return 0;
}
方法二 —— 正向比较(双指针)
分析
从前往后比较
和反向比较差不多,只不过移动的是b指针,即当b指针指向的和a指针指向的不一样时,说明a中编号为b[j]的奶牛必定要移动,那么标记编号为b[j]的奶牛,b指针向后寻找,a指针指向第一个没被标记过的奶牛
代码:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100010;
int a[N];
int b[N];
bool st[N]; //标记奶牛是否移动过
int n;
//双指针 -- 正向思维
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
int res=0,i=1; //i指向a中的奶牛
for(int j=1;j<=n;j++)
{
//指向a中第一头未被标记的牛
while(st[a[i]]&&i<=n) i++;
//如果指向的两头奶牛不一样,操作数++
if(a[i]!=b[j]) res++;
//标记b[j]
st[b[j]]=1;
}
cout<<res<<endl;
return 0;
}