一、概述
只交换0和其他数,将数列变为有序。
本题的难点在于当数据量较大时如何保证不超时。
我就是个弱智,想的太直了,我的算法优化到最后也还有一个测试点超时。
先说明自己的算法,再说明AC的算法。
二、分析
由于只允许交换0和其他数,因此基础算法就是交换0和0所在的数组元素的下标。但是当0在0的时候,问题就出现了,下一步不知如何交换。这时应找到最小的不在正常位置的元素,然后将0与它交换,接着继续交换,直到所有元素都在正确位置。
我从第一步开始就选错了方向。
1、我的方法
开一个数组,按输入顺序保存数列。然后遍历数列计算目前在正常位置的数字的个数sum。
如果sum等于数字数量N,那么直接输出0,不用交换;
如果sum不等于N,这有一点要注意,如果0的位置是0,那么sum要减一,因为会把在正确位置的0交换出去,如果0的位置不是0,那么就不用交换。
设置一个最小的不在正常位置的值minwrong,初始值为1。
因为当0不在0位置时,我们要寻找0所在位置的元素下标,如果从0开始遍历太浪费时间了,从minwrong开始遍历能好一点;
同时当0在0位置时,我们要寻找最小的不在正常位置的值,也就是minwrong,这样也避免了从头开始遍历,
如下:
if(sum==N)
printf("0");
else
{
if(Number[0]==0)
sum--;
while(sum!=N-1)
{
if(Number[0]!=0)
{
int j=minwrong;
if(Number[0]==zero)
j=0;
else
{
while(Number[j]!=zero)
j++;
}
if(j==minwrong)
minwrong++;
swap(&Number[j],&Number[zero]);
zero=j;
swapnum++;
sum++;
}
else
{
int j=minwrong;
while(Number[j]==j)
j++;
minwrong=j;
swap(&Number[0],&Number[j]);
zero=j;
swapnum++;
}
}
同时注意bug,当0不在0位置时,首先要判断0位置的元素是不是要找的,然后再从minwrong开始遍历,否则会超时。
这样就计算出了总的使用次数。
代码如下:
#include<stdio.h>
#include<cstdio>
#include<string>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int Number[100010]={0};
void swap(int *a,int *b)
{
int c=*b;
*b=*a;
*a=c;
}
int main()
{
int N;
int i;
scanf("%d",&N);
for(i=0;i<N;i++)
{
scanf("%d",&Number[i]);
}
int zero=0;
while(Number[zero]!=0)
zero++;//zero存储目前0的下标
int sum=0;
for(i=0;i<N;i++)
if(Number[i]==i)
sum++;
int swapnum=0;
int minwrong=1;//最小的错误的的下标
if(sum==N)
printf("0");
else
{
if(Number[0]==0)
sum--;
while(sum!=N-1)
{
if(Number[0]!=0)
{
int j=minwrong;
if(Number[0]==zero)
j=0;
else
{
while(Number[j]!=zero)
j++;
}
if(j==minwrong)
minwrong++;
swap(&Number[j],&Number[zero]);
zero=j;
swapnum++;
sum++;
}
else
{
int j=minwrong;
while(Number[j]==j)
j++;
minwrong=j;
swap(&Number[0],&Number[j]);
zero=j;
swapnum++;
}
}
printf("%d",swapnum);
}
}
测试点2运行超时。
2、AC方法
首先,要对输入的数列进行处理,数组中存储的不是直观的输入顺序的元素,而是元素的位置。
举例如下:
输入的序列为3 5 7 2 6 4 9 0 8 1
我的算法的数组从头到尾的顺序为3 5 7 2 6 4 9 0 8 1
而AC方法的数组的顺序为7 9 3 0 5 1 4 2 8 6
如下:
int N;
int i;
scanf("%d",&N);
for(i=0;i<N;i++)
{
int num;
scanf("%d",&num);
Number[num]=i;//设下标为m,值为n,则意义为数字m现在的位置为n
}
这样一来,首先减少了一大块时间,即寻找目前0的下标所在的数组的下标,直接用Number[Number[0]]表示即可。很高的提升了效率。交换的框架类似:当0不在0位置时,交换,当0在0位置时,从最小的不在原位置的元素开始找,一直找到真正的不在原位置的元素,交换,这样0又不在原位置了。这个算法的精妙之处就在于,最后最极端,搜索不在原位置的元素的次数也不超过N,而不是我的算法一样的N^2。一些注意点倒是和我的一样。
如下:
if(sum==N)
printf("0");
else
{
while(sum!=N-1)
{
if(Number[0]==0)
sum--;
while(Number[0]!=0)
{
//if(sum==N-1)
//break;
//if(minwrong==Number[0])
//minwrong++;
swap(&Number[0],&Number[Number[0]]);//有点难理解,eg.现在0的位置是3,3的位置是5,那么swap之后,0的位置是5,3的位置是3
swapnum++;
sum++;
}
if(Number[0]==0)
{
if(sum==N-1)
break;
while(minwrong<N)
{
if(Number[minwrong]!=minwrong)
{
swap(&Number[0],&Number[minwrong]);
swapnum++;
break;
}
minwrong++;
}
}
}
之后输出即可。
三、总结
我从一开始的想法就偏了,想的太简单,应该换位思考才是。实际上我自己八成也想不到AC代码= =
PS:代码如下:
#include<stdio.h>
#include<cstdio>
#include<string>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int Number[100010]={0};
void swap(int *a,int *b)
{
int c=*b;
*b=*a;
*a=c;
}
int main()
{
int N;
int i;
scanf("%d",&N);
for(i=0;i<N;i++)
{
int num;
scanf("%d",&num);
Number[num]=i;//设下标为m,值为n,则意义为数字m现在的位置为n
}
int sum=0;
for(i=0;i<N;i++)
if(Number[i]==i)
sum++;
int swapnum=0;
int minwrong=1;//最小的错误的的下标
if(sum==N)
printf("0");
else
{
while(sum!=N-1)
{
if(Number[0]==0)
sum--;
while(Number[0]!=0)
{
//if(sum==N-1)
//break;
//if(minwrong==Number[0])
//minwrong++;
swap(&Number[0],&Number[Number[0]]);//有点难理解,eg.现在0的位置是3,3的位置是5,那么swap之后,0的位置是5,3的位置是3
swapnum++;
sum++;
}
if(Number[0]==0)
{
if(sum==N-1)
break;
while(minwrong<N)
{
if(Number[minwrong]!=minwrong)
{
swap(&Number[0],&Number[minwrong]);
swapnum++;
break;
}
minwrong++;
}
}
}
printf("%d",swapnum);
}
/*int N;
int i;
scanf("%d",&N);
for(i=0;i<N;i++)
{
scanf("%d",&Number[i]);
}
int zero=0;
while(Number[zero]!=0)
zero++;//zero存储目前0的下标
int sum=0;
for(i=0;i<N;i++)
if(Number[i]==i)
sum++;
int swapnum=0;
int minwrong=1;//最小的错误的的下标
if(sum==N)
printf("0");
else
{
if(Number[0]==0)
sum--;
while(sum!=N-1)
{
if(Number[0]!=0)
{
int j=minwrong;
if(Number[0]==zero)
j=0;
else
{
while(Number[j]!=zero)
j++;
}
if(j==minwrong)
minwrong++;
swap(&Number[j],&Number[zero]);
zero=j;
swapnum++;
sum++;
}
else
{
int j=minwrong;
while(Number[j]==j)
j++;
minwrong=j;
swap(&Number[0],&Number[j]);
zero=j;
swapnum++;
}
}
printf("%d",swapnum);
}*/
}