F题:松鼠排序
松鼠宝宝有一排n个大小不一的坚果,松鼠宝宝想把坚果从小到大排序,每次他会选择两个坚果a和b每次花费1点力气把这两个坚果交换,爱动脑筋的松鼠宝宝想知道他排完这n个坚果一共需要花费的最少力气是多少?
输入描述:
第一行一个整数n代表坚果数
接下来一行n个整数代表每个坚果的大小(每个坚果大小都不一样,即大小为1-n的一个排列)
1<=n<=1e5
坚果大小x,1<=x<=n
输出描述:
一行输出代表松鼠宝宝花费的最小力气
我们换成简单的话就是,将有序序列1,2,3.....n 随机打乱,每次操作可以交换任意两个数字,求最少的操作数使得这个序列回复原状。
我的思路就是,每次找到这个序列里面最小的数对应的位置(下标),然后把它和它原本位置上的数字交换。再找次小的数....交换....以此类推直到有序
假如为 5 4 3 2 1,最小的数是1,其下标为(5) 我们把它和 a[1](原本应该在的位置)交换
swap ( a[1] , a[5] ); swap ( a[2] , a[4] ); swap ( a[3] , a[3] ); 注意,如果这个数在它原本的位置上,那么不计入答案。
所以应该是2种。
然后我们就能给出这样的代码:
int ans;
int a[N];
int b[N];
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
}
for (int i = 1; i <= n; i++)
{
int* it = find(a, a + n, i);//因为i从1开始,即就是从原有的序列开始,所以i是当前最小值
swap(a[i], a[it - a]);
if (i != it - a)ans++;
}
cout << ans << endl;
}
find 函数 是 查找 这个元素i 在序列 a 中的位置(下标) 然后返回这个下标的地址(16进制),我们要用它减去序列a的首地址,就得到 i 的下标了(10进制)
但是!!!find函数是O(n) 而外层还有一层循环 O(n) 也就是 O(n^2) 的时间复杂度无法通过本题。
在这样的基础上,我们加一个数组 b,用来存放 a中每个数字出现的位置。来代替find函数,这样就能达到O(n)的复杂度。
const int N = 1e5 + 10;
int ans;
int a[N];
int b[N];
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
b[a[i]] = i; 一个数组用来存放 a数组每个数出现的位置
}
for (int i = 1; i <= n; i++)
{
int t=a[i],k=b[i]; 在每次循环之前,存好这两个值,因为后面交换会改变值。
b[i]指的是 i (即当前的最小值) 在a中的位置
a[k] 即 a[b[i]],其值是i
if(t!=a[k]) ans++;
swap(a[i], a[k]); 我们要把i换到原来的位置上:
swap(b[i],b[t]); 更新 a数组中每个数出现的位置
}
cout << ans << endl;
}
这两份代码的区别就是,我们手动用另一个数组实现了find函数的功能。
G题:
给定一个长度为n的01串,你需要选择一段任意长度(可以为0)的区间对其翻转,翻转后,求最长的一段连续的全是1的区间的长度。输入描述:
输入共2行。 第一行一个整数n(1≤n≤106)n(1\leq n \leq 10^6)n(1≤n≤106)。 第二行一个长度为n的01序列。输出描述:
输出一个整数,表示最长的长度。
我的思路是参考了我们学校选拔赛的C题,那题的答案非0即1,是一个找规律的题,找规律到最后发现就两种情况。
那么这题的思路也是类似的,无论你这个01序列是怎样的,我们只需要找出这里面最长的两段1就可以了。因为我们是选择一段任意长度的区间,所以始终可以把两段最长的1,拼接到一起。
那么现在的问题就是如何找到两端最长的1序列了。
1,双指针(好丑)
const int N =1e6+10;
int len[N],n,t;
string s;
int main()
{
cin>>n; cin>>s;
for(int i=0,j;i<n;i++ )
{
int ans=0;
if(s[i]=='1')
{
for( j=i;j<n;)
{
if(s[j]=='0')break;
else if(s[j]=='1')
{
ans=j-i+1;
j++;
}
}
i=j;
len[t++]=ans;
}
}
sort(len,len+t);
cout<<len[t-1]+len[t-2];
}
2,朴实无华的计数法(在输入01序列的时候就开始计数最长的序列)
const int N =1e6+10;
int len[N],t=0,ans=0,n;
char temp[N];
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>temp[i];
if(temp[i]=='1')
{
ans++; len[t]=ans;
}
else
{
ans=0; t++;
}
}
sort(len ,len+t+1);
cout<<len[t]+len[t-1];
}