关于排序_重点是冒泡
排序有很多种但是 就看你会哪一种了!
选择排序 O(𝑛2)
插入排序 O(𝑛2)
冒泡排序 O(𝑛2)
计数排序 O(𝑛 + 𝑚)
基数排序 O (𝑛 log 𝑚)
桶排序 O(𝑛)~O(𝑛2)
归并排序 O(𝑛 log 𝑛)
堆排序 O(𝑛 log 𝑛)
快速排序 O(𝑛 log 𝑛)
值得注意的是其中基数排序是我们(或者我)认为是桶排序,但其实并非如此真名是基数排序复杂度(n+m);
如果数字过大的话,值得一提的是最后三种排序使我们最快的排序方法了没有更快的!
如果你发现一种排序方法比最后三种还快(在数字非常大的情况下)那么说明你的算法错了!
下面上比较有意思的例题:
这道题呢 很有趣,经过思考之后。肯定是要排序但是按照左端点或者右端点进行排序后的衔接算法我们可以考虑dp,或者也可能是贪心对吧
方法1:贪心
按左端点排序,此时考虑贪心维护当前工作数最多且右端点最小。如果当前左端点小于p 那么贪心 任务数++
此时对后面的部分任务(指在左端点在p的右边)那么这些任务我们是做不了的,它们能做的贡献只有p=min(p,b[i]);
仔细思考一定是最优的!因为每可以开创一个任务时是最优的不能开创时对当前任务我们可以尽量维护p最小呢。
这里是讲每个开创一个任务作为子阶段。
方法2:dp
f[i]表示前i个任务最多能做多少件 此时就有了状态转移方程 f[i]=max(f[i-1],f[j]+1);其实就是当前的 任务做与不做。
状态要讲究顺序性如何让其有序,处理方法其实是按照右端点排序。因为完成一个任务右端点结束了才算完成。
f[j]选个最大的吧,那么单调性优化?不太行!因为左端点不具有单调性。
考虑f[j]在什么时候是最优的显然对于任意时刻 f[j+1]>=f[j]也就是越靠前越优秀。
这时我们只需查找离当前任务左端点右边第一个j这个就是最优且合法的!(当然你得合法了)
然后考虑左端点单调所以可以二分查找!
综上两种方法都是基于排序的 复杂度nlogn
冒泡排序:
冒泡之所以重要呢是因为它和逆序对有关,这就比较烦了!
首先冒泡排序的交换顺序==此排列的逆序对个数(因为每次交换最多消除一个逆序对)
设m是冒泡执行的轮数 m=max(每个数前面有几个比它大的);显然比较次数=(m-1)*n
于是下面有例题:
这到题是一个裸的求逆序对个数可是呢,树状数组可以正着存也可以倒着存。
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<iomanip> #include<cstring> #include<string> #include<cstdlib> #include<cmath> #include<algorithm> #include<cctype> #include<utility> #include<set> #include<bitset> #include<queue> #include<stack> #include<deque> #include<map> #include<vector> #include<ctime> #define INF 2147483646 using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline long long read() { long long x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } inline void put(long long x) { x<0?putchar('-'),x=-x:0; long long num=0;char ch[50]; while(x)ch[++num]=x%10+'0',x/=10; num==0?putchar('0'):0; while(num)putchar(ch[num--]); putchar('\n');return; } const long long MAXN=500002; long long n; long long a[MAXN],b[MAXN],c[MAXN],maxx=500000; long long t=0,cnt=0; void discrete() { sort(b+1,b+1+n); for(long long i=1;i<=n;i++)if(i==1||a[i]!=a[i-1])b[++t]=b[i]; for(long long i=1;i<=n;i++)a[i]=lower_bound(b+1,b+1+t,a[i])-b; } void add(long long x,long long y){for(;x;x-=x&(-x))c[x]+=y;} long long ask(long long x) { long long cnt=0; for(;x<=MAXN;x+=x&(-x))cnt+=c[x]; return cnt; } int main() { //freopen("1.in","r",stdin); while(1) { n=read();cnt=0;t=0; memset(c,0,sizeof(c)); if(n==0)break; for(long long i=1;i<=n;i++)a[i]=read(),b[i]=a[i]; discrete();//由于离散 所以可以不用管a[i]=0; for(long long i=1;i<=n;i++) { cnt+=ask(a[i]+1); add(a[i],1); } put(cnt); } return 0; }
#include<iomanip> #include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<vector> #include<stack> #include<queue> #include<map> using namespace std; inline long long read() { long long x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const long long maxn=500008; long long c[maxn],a[maxn],maxx=-1,ans[maxn],b[maxn]; long long n,m,tot=0; long long lowbit(long long x) { return x&(-x); } void add(long long x,long long y) { for(;x<=n;x+=lowbit(x))c[x]+=y; } void discrete() { sort(b+1,b+1+n); long long m=unique(b+1,b+1+n)-b-1; for(long long i=1;i<=n;i++) a[i]=lower_bound(b+1,b+m+1,a[i])-b; } long long sum(long long x) { long long tot=0; for(;x;x-=lowbit(x))tot+=c[x]; return tot; } int main() { // freopen("1.in","r",stdin); n=read(); for(long long i=1;i<=n;i++)a[i]=read(),b[i]=a[i]; discrete(); for(long long i=n;i>=1;i--) { add(a[i],1); ans[i]=sum(a[i]-1); } for(long long i=1;i<=n;i++)tot+=ans[i]; printf("%lld\n",tot); return 0; }
显然上面正着存比较容易理解但是我写了个倒着存的晕死了!
gb:
#include<iostream> #include<string> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<iomanip> #include<ctime> #include<vector> #include<queue> #include<map> #include<stack> using namespace std; inline long long read() { long long x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10 +ch-'0';ch=getchar();} return x*f; } const long long maxn=500002; long long n,ans=0; long long a[maxn],tmp[maxn]; void gb(long long l,long long r) { if(l==r)return; long long mid=(l+r)>>1; gb(l,mid);gb(mid+1,r); long long i=l,j=mid+1; for(long long k=l;k<=r;k++) { if(j>r||(i<=mid&&a[i]<=a[j]))tmp[k]=a[i++]; else tmp[k]=a[j++],ans+=mid-i+1; } for(long long k=l;k<=r;k++)a[k]=tmp[k]; } int main() { //freopen("1.in","r",stdin); n=read(); for(long long i=1;i<=n;i++)a[i]=read(); gb(1,n); printf("%lld\n",ans); return 0; }
这道题就很绕了反正我头有点晕。。
两次冒泡 鬼知道它要干什么 复杂度明明是一样的!搞什么事情!
好题 但我真的头很疼 什么鬼题我觉得很不可写,但是结论是:
将其离散然后答案是max{前i个位置>i的数有多少个};
很疑惑对不对,但是仔细思考发现没有什么问题。想了至少3h 英语课 化学课 晚上睡觉闭着眼也在体会这个过程!
针对一个最大的移动次数m 我们把i这个位置前面m个数还原 后面m个数还原 然后整个序列就完整了!
为什么 反证法:考虑此时序列仍出现逆序那么在逆序的地方一定是 m+1~i 或者 i~n-m 之间 那么有逆序证明
当前取m+1的数字并没有被还原当前这个逆序的地方可以承接m,仔细思考出现了max+1 故与原命题不符!
所以 证毕,结论正确。哇塞这个好难想啊!尤其是离散后的东西巧妙借助了离散和树状数组!
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<iomanip> #include<cstring> #include<string> #include<cstdlib> #include<cmath> #include<algorithm> #include<cctype> #include<utility> #include<set> #include<bitset> #include<queue> #include<stack> #include<deque> #include<map> #include<vector> #include<ctime> #define INF 2147483646 #define xx t[i].x #define yy t[i].y #define sum t[i].id #define up(p,i,n) for(int i=p;i<=n;i++) using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } inline void put(int x) { x<0?putchar('-'),x=-x:0; int num=0;char ch[50]; while(x)ch[++num]=x%10+'0',x/=10; num==0?putchar('0'):0; while(num)putchar(ch[num--]); putchar('\n');return; } const int MAXN=100002; int n,cnt=0,ans=1,tt=0; int c[MAXN],maxx=100000; struct wy { int x,id,y;//y是离散值 }t[MAXN]; int cmp(wy s,wy k){return s.x<k.x;} int cmp1(wy s,wy k){return s.id<k.id;} void add(int x,int y){for(;x;x-=x&(-x))c[x]+=y;} int ask(int x) { int cnt=0; for(;x<=maxx;x+=x&(-x))cnt+=c[x]; return cnt; } int main() { //freopen("1.in","r",stdin); n=read(); up(1,i,n)xx=read(),sum=i; sort(t+1,t+1+n,cmp); t[n+1].x=INF; //up(1,i,n)put(xx); up(1,i,n) { cnt=i;tt=1; while(xx==t[i+1].x)i++,tt++; for(int j=cnt;j<=i;j++)t[j].y=ans; ans+=tt; } //up(1,i,n)put(yy); sort(t+1,t+1+n,cmp1); //up(1,i,n)put(yy); ans=1;//仔细读题发现 至少答案为1 up(1,i,n) { add(yy,1); ans=max(ans,ask(i+1)); } put(ans); return 0; }
还有更难的,这个才是gold。还有一个platinum(铂金)。
怎么这么难啊,思维难度好高,首先题目就很不好理解。
题目意思当分出分割点的时候当前序列内部的数字都小于外部的数字。
那么这道题求的是对整个序列进行这样的冒泡+分割点就是答案了。
考虑每个点对答案的贡献 是每个点出现间隔的时间就是对答案的贡献。
出现一个分割点还不够因为只要当前集合>1那么这个点还会对答案进行贡献。此时考虑当前这个点两边都有 分割点时就是对答案进行的贡献了!
那么就可以开始算了我们设 t[i]表示前i个数已经有序(注意不一定排好了)因为间隔点出现的时间比当前数字有序快一点。
了所需要的时间 然后 ans+=(t[i],t[i-1]);其中t[i]>=1因为至少要允许允许一次机器。
现在考虑 每个t[i]怎么求啊这才是重点 我想要是真的求的话自己能想出来不过思维就比较痛苦了!
但是作为一个 坚持面对困难的人 我想 应该去这么做!横刀立马斩题于马下。
分析问题的特异性——冒泡。然后 t[i]等于什么呢 先这样 离散一下 考虑 第i个数字的位置在哪 。
想让前i个数字有序的话一定是 离i这个位置最远的 的那个数到达i这个位置那么前i个数就有序了。
这样 t[i]=max(pos[j])-i;其中 1=<j<=i;
那么这个问题就得到解决了其实不离散也是可以的!我们只需要知道当前的相对位置pos[j]是指在j这个位置的数字原来的位置是多少当前它要移动到i这个位置了!
离散 也不能瞎离散 就是 有多个值都相同且不区分排名也是不行的,因为 pos[j]每个j都只能对应一个pos[j]尽管j相同!
所以离散不太行啊,也没用直接sort完事千万不要不明白写个离散化然后把自己给绕晕了才是。
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<iomanip> #include<cstring> #include<string> #include<cstdlib> #include<cmath> #include<algorithm> #include<cctype> #include<utility> #include<set> #include<bitset> #include<queue> #include<stack> #include<deque> #include<map> #include<vector> #include<ctime> using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline long long read() { long long x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } inline void put(long long x) { x<0?putchar('-'),x=-x:0; long long num=0;char ch[50]; while(x)ch[++num]=x%10+'0',x/=10; num==0?putchar('0'):0; while(num)putchar(ch[num--]); putchar('\n');return; } const long long MAXN=100002; long long n,maxx=0,ans=0; long long pos[MAXN]; struct wy { long long id,v; inline friend long long operator < (const wy x,const wy y) { return (x.v==y.v?x.id<y.id:x.v<y.v); } }t[MAXN]; int main() { n=read(); for(long long i=1;i<=n;i++) { t[i].v=read(); t[i].id=i; } sort(t+1,t+1+n); //for(long long i=1;i<=n;i++)cout<<t[i].v<<endl; for(long long i=1;i<=n;i++) { maxx=max(maxx,t[i].id); pos[i]=max(1ll,maxx-i); } for(long long i=1;i<=n;i++)ans+=max(pos[i],pos[i-1]); put(ans); return 0; }
然后就圆满结束了这是针对冒泡的智商题。真考验智商。