【Usaco 2010 NOV Gold】奶牛的图片
2016-01-24 17:22
211 查看
Description
Farmer John希望给他的N(1<=N<=100,000)只奶牛拍照片,这样他就可以向他的朋友炫耀他的奶牛.这N只奶牛被标号为1..N.在照相的那一天,奶牛们排成了一排.其中第i个位置上是标号为c_i(1<=c_i<=N)的奶牛.对于奶牛的站位,Farmer John有他自己的想法.
FJ是这么想的,标号为i(1<=i<=n-1)的奶牛只能站在标号为i+1的奶牛的左边,而标号为N的奶牛只能站在标号为1的奶牛的左边.当然,没有牛可以站在队列中最左边的奶牛的左边了.也就是说,最左边的奶牛编号是随意的.
这些奶牛都非常的饿,急切的希望吃到FJ承诺的在拍照后的大餐,所以FJ想尽快的拍照.奶牛们的方向感非常的不好,所以FJ每一分钟只可以选择相邻的两只奶牛然后让他们交换位置.FJ最小需要多少时间就能使奶牛站成一个可以接受的序列?
比方说一个有5只奶牛的例子,一开始序列是这样的:
左边 右边
3 5 4 2 1
第一分钟,FJ可以交换第二队奶牛(即5和4),交换后的队列:
3 4 5 2 1
第二分钟,FJ交换最右边的一对,序列变成这样:
3 4 5 1 2
这样,只用了2分钟,就是序列变为了一个FJ所希望的序列.
分析
对于这道题,我们其实是很难入手的,而这时我们先想想普通的暴力是什么?最普通的是将每个1..n的序列求出来,然后求答案,但是这是O(n^2),而且很难优化。
所以我们可以先考虑特殊情况:
一个1..n的乱序的序列变成一个有序的序列,我们很容易想到是求这个序列的逆序对的个数。
而对此以外,好像没有什么其他可以利用的信息。
但是认真分析,其实是有关联的:
对于3 5 4 2 1 -> 1 2 3 4 5 逆序对:ans=8
然后3 5 4 2 1 -> 2 3 4 5 1 逆序对:ane=?
……
这时我们可不可以也把2 3 4 5 1看成有序的呢?其实是可以的,那么就是2 3 4 5 max
前面的序列就是3 4 5 2 max,这时的逆序对与上面的那个序列实际上来说就是
上面的序列除去1后产生的值加上max会造成的逆序对的数量。
也就是ane=8-4+0。再分析这样推下去是可以推再下层的,为什么?
关键是因为我们都是对于上一层的答案进行改变,而且每次改变都是改最小的那个变成最大的。
有人可能会问第一次是操作最小的,那第二次的2不是>1吗?但是1已经变成了max,所以2是此时的最小的。
所以我们可以总结成ane=ans-(p[i]-1)+(n-p[i]),p[i]表示数字i的位置。
说真的,这题真的很妙,很巧!于是我趁这次机会研究了一下逆序对的问题
首先,为什么逆序对的组数等于原序列交换成从小到大的最小次数呢?
伪证明1:
首先有ai>aj且i<=j,那么若想把这序列改成排序后的序列,那么这两个数必须交换,即次数必会加1.
再或者这样理解:每次交换一对数,一定会使逆序对数目加或减1,而最后的序列的逆序对数为0,所以最优解都是-1.
伪证明2:
与排序过的序列以外的序列都被定义为没排序的,而这样必定会有逆序对。
其次为什么一定可以交换逆序对的组数呢?(即相邻的交换)
这个问题留给读者。
而逆序对的计算可以用树状数组或者归并排序。
原谅我太懒了,只能放个标程了。。大家凑合理解吧。
代码1(归并版)
[code]#include<iostream> #include<cmath> #include<cstring> #include<cstdio> #include<cstdlib> #include<algorithm> #define ll long long using namespace std; const int N=100005; ll n,a ,t[N*3],b ,ans,i,j,tot,c ; void merge(ll l,ll r,ll a ){ if (l>=r) return; int mid=(l+r)>>1; merge(l,mid,a); merge(mid+1,r,a); i=l;j=mid+1; tot=0; while(i<=mid&&j<=r){ if(a[i]>a[j]) { c[++tot]=a[j]; ans+=mid-i+1; j++; }else { c[++tot]=a[i]; i++; } } for(int q=i;q<=mid;q++) c[++tot]=a[q]; for(int p=j;p<=r;p++) c[++tot]=a[p]; for(int i=l,j=1;i<=r;i++,j++) a[i]=c[j]; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); b[a[i]]=i; } merge(1,n,a); ll sum,dat; dat=ans; sum=ans; for(int i=1;i<=n;i++){ sum-=(b[i]-1);sum+=(n-b[i]); dat=min(dat,sum); } printf("%lld",dat); }
代码2(树状数组)
[code]#include<iostream> #include<cmath> #include<cstring> #include<cstdio> #include<cstdlib> #include<algorithm> #define ll long long using namespace std; const int N=100005; ll n,a ,t[N*3],b ,ans; void find(int k,int l,int r,int x,int y){ if (l==x&&r==y) ans+=t[k]; else { int mid=(l+r)>>1; if (mid<x) find(k*2+1,mid+1,r,x,y);else if (mid>=y) find(k*2,l,mid,x,y);else { find(k*2,l,mid,x,mid); find(k*2+1,mid+1,r,mid+1,y); } } } void insert(int k,int l,int r,int x){ if (l==r) t[k]++; else { int mid=(l+r)>>1; if (mid<x) insert(k*2+1,mid+1,r,x); else insert(k*2,l,mid,x); t[k]=t[k*2]+t[k*2+1]; } } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); b[a[i]]=i; find(1,1,n,a[i],n); insert(1,1,n,a[i]); } ll sum,dat; dat=ans; sum=ans; for(int i=1;i<=n;i++){ sum-=(b[i]-1);sum+=(n-b[i]); dat=min(dat,sum); } printf("%lld",dat); }
相关文章推荐
- hdu1847 Good Luck in CET-4 Everybody!
- 【POJ 2195】 Going Home(KM算法求最小权匹配)
- HDU-4982-Goffi and Squary Partition【贪心】【构造】
- HMM学习笔记—002--维特比算法(viterbi algorithm)
- Atom插件go-plus的离线安装
- OC基础之Category,Extension,Protocol
- uva11292 - The Dragon of Loowater (贪心)
- gitlab和Django实现push自动更新
- gitlab和Django实现push自动更新
- gitlab和Django实现push自动更新
- 张江男的逆袭,我如何使用leangoo提升团队效率
- BZOJ 1419: Red is good|期望Dp
- 在visual studio 2013下使用Google Test
- 1419: Red is good 概率与期望 DP
- 搭建go语言idea开发环境
- connect-mongo:Error: Connection strategy not found
- 谷歌应用商店Google Play即将重返中国
- 谷歌应用商店Google Play即将重返中国
- 《Learn You Some Erlang for Great Good!》的学习笔记(二)
- Django学习系列—第二天