首先,这场比赛规定是不能使用stl的,所以让用惯了各种stl的我一开始难以适应。既然是排序专题,那么自然就少不了快排了。在这里先贴个手写快排的模板,当然也是这场比赛的主旋律。
int quickSort(int a[],int low ,int high)
{
int i = low;
int j = high;
int temp = a[low];
while (i < j)
{
while (i < j && a[j] >= temp)
{
j -- ;
}
if(i<j)
{
a[i] = a[j] ;
}
while(i < j && a[i] < temp)
{
i ++ ;
}
if(i < j)
{
a[j] = a[i] ;
}
}
a[i] = temp ;
if(i > low) quickSort(a, low, i-1) ;
if(i < high) quickSort(a , i+1, high) ;
}
1001
HDU 1029
题意很简单,一开始没注意要求,直接map标记一下,给A掉了,然后瞬间领悟不能用stl。神马sort,vector统统地不要用了。
又手写了个快排,然后悲剧的发现tle了。证明我的快排还是写的略挫,然后也就死脑筋的在想排序方面的方法,当然最后也没改进。
比赛结束之后,看到别人用另一种方法做出来,大概10行,方法略屌,就不叙述了,想看的可以直接去hdu1029的discuss里面看。
当然这里主要讲的还是我发现其实这题还是可以用树来搞的,然后就去学了SBT(其他树也可以,主要是SBT实现代码简单),SBT的高度是o(logn),maintain是o(1),所有主要操作都是o(logn),其中就包括了select(t,k)(选择第k小数)。
分析一下本题,不难发现这题的答案就是将所有数排序一遍然后输出中间那个数就可以了。
那这正好和select(t,k)的功能相匹配了,即n个无序的数字,insert进SBT之后,只要输出select(t,n/ 2 + 1)即可。而这个操作只需o(logn),显然可行。
关于SBT的其他操作实现原理,请具体参考陈启峰大神的论文。下面只给出简单注释。
(sort100ms,SBT400ms)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cmath>
#include <cstring>
#include <queue>
#include <set>
#include <vector>
#include <stack>
#include <map>
#include <iomanip>
#define PI acos(-1.0)
#define Max 1000005
#define inf 1<<28
#define LL(x) (x<<1)
#define RR(x) (x<<1|1)
#define FOR(i,s,t) for(int i=(s);i<=(t);++i)
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
#define mp(a,b) make_pair(a,b)
using namespace std;
struct SBT
{
int num,left,right,size;
} tree[Max];
int root,top;
void left_rot(int &x)//左旋
{
int y = tree[x].right;
tree[x].right = tree[y].left;
tree[y].left = x;
tree[y].size = tree[x].size;//转上去的节点数量为先前此处节点的size
tree[x].size = tree[tree[x].left].size + tree[tree[x].right].size + 1;
x = y;
}
void right_rot(int &x)//右旋
{
int y = tree[x].left;
tree[x].left = tree[y].right;
tree[y].right = x;
tree[y].size = tree[x].size;
tree[x].size = tree[tree[x].left].size + tree[tree[x].right].size + 1;
x = y;
}
//当往树里插入数字的时候,将会导致无法满足SBT的性质。
void maintain(int &x,bool flag)
{
if(!flag)//左边
{
if(tree[tree[tree[x].left].left].size > tree[tree[x].right].size)//case 1 :左子树的左孩子大于右子树
right_rot(x);
else if(tree[tree[tree[x].left].right].size > tree[tree[x].right].size)//case 2:左子树的右孩子大于右子树
{
left_rot(tree[x].left);
right_rot(x);
}
else return;
}
else //右边
{
if(tree[tree[tree[x].right].right].size > tree[tree[x].left].size)//case 3:右孩子的右子树大于左孩子
left_rot(x);
else if(tree[tree[tree[x].right].left].size > tree[tree[x].left].size)//case 4:右孩子的左子树大于左孩子
{
right_rot(tree[x].right);
left_rot(x);
}
else return;
}
maintain(tree[x].left,0);
maintain(tree[x].right,1);
maintain(x,1);
maintain(x,0);
}
void insert(int &x,int num)
{
if(x == 0)
{
x = ++top;
tree[x].left = tree[x].right = 0;
tree[x].size = 1;
tree[x].num = num;
}
else
{
tree[x].size ++;
if(num < tree[x].num) insert(tree[x].left,num);//小于当前的值,则将num插入左子树
else insert(tree[x].right,num);//否则插入到右子树中
maintain(x, num >= tree[x].num);//每次插入都进行一次维护。 0 代表左子树维护 , 1 代表右子树维护。
}
}
int select(int &x,int k)//求第k小数
{
int r = tree[tree[x].left].size + 1;//x的左子树的总和加上他本身
if(r == k) return tree[x].num;//如果r == k 的话,那么x的元素就是第k小数
else if(r < k) return select(tree[x].right,k - r);//如果r < k 的话,则继续往右子树找 第 k - r 小数,不难理解。
else return select(tree[x].left,k);//如果 r > k 的话, 则继续往左子树里面搜索第k小数。
}
int main()
{
int x ;
int n ;
while(cin >> n)
{
root = top = 0;
for (int i = 0 ; i < n ; i++)
{
scanf("%d",&x);
insert(root , x);
}
printf("%d\n",select(root , n / 2 + 1));//输出最中间的数
}
return 0;
}
当然SBT还有好多其他操作,可自行百度。
1002
HDU 1040 直接用楼顶的手写快排可过,无任何贴代码的意义。
1003
HDU 1106 同样大水题。
1004
HDU 1209 这道题写的时候被坑了一下,角度的数据类型搞错了,看题太不仔细,一开始看成分钟都是5的倍数,后来再读一遍发现哪有这句话啊,当时真不知道怎么想的。还是贴一下代码吧。
struct kdq
{
double x;
int n;
int d;
} jiaodu[10];
int main()
{
int n ;
while(cin >> n)
{
while ( n--)
{
int h , m;
char a[100][100];
for (int i = 0; i < 5; i ++)
{
scanf("%s",a[i]);
sscanf(a[i],"%d:%d",&h,&m);
jiaodu[i].d = h*60 + m;
if(h > 12) h -= 12;
double hh = (h + m/60.0)*30.0;
double mm =m*6.0;
jiaodu[i].x = min(abs(hh - mm),360 - abs(hh - mm));
jiaodu[i].n = i;
}
for (int i = 0; i < 5; i ++)
{
for (int j = i + 1; j < 5 ; j ++)
{
if (jiaodu[i].x > jiaodu[j].x)
{
swap(jiaodu[i],jiaodu[j]);
}
}
}
for (int i = 0 ; i < 5 ; i ++)
{
for (int j = i+1; j < 5; j ++)
{
if( jiaodu[i].x == jiaodu[j].x&&jiaodu[i].d>jiaodu[j].d)//当角度相同,按时间早的排,这个一开始也没注意到。
swap(jiaodu[i],jiaodu[j]);
}
}
printf("%s\n",a[jiaodu[2].n]);
}
}
}
1005
HDU 1236
主要就是学号的升序排序。不能用stl,我就用了一种超笨的方法。给跪。先一遍快排按照分数排,然后冒泡排一次学号,竟然没T。
int score[1000];
struct aaaa
{
char name[1000];
int score;
} mm[10000];
int quickSort(aaaa a[],int low,int high)
{
int i = low ;
int j = high;
int temp = a[i].score;
while(i<j)
{
while(i<j && a[j].score< temp)
j--;
if(i<j)
swap(a[i],a[j]);
while(i < j && a[i].score>= temp)
i++;
if(i<j)
swap(a[i],a[j]);
}
a[i].score = temp;
if(i > low )quickSort(a,low,i-1);
if(i < high)quickSort(a,i+1,high);
return 1;
}
int main()
{
int N,M,G;
while(cin >> N ,N)
{
cin>>M>>G;
int ans =0 ;
for (int i = 1 ; i <= M ; i ++)cin >> score[i];
for (int i = 0 ; i < N ; i ++)
{
int n ;
scanf("%s",mm[i].name);
scanf("%d",&n);
mm[i].score=0;
while(n--)
{
int a;
scanf("%d",&a);
mm[i].score += score[a];
}
if(mm[i].score >= G)ans ++;
}
int l = strlen(mm[0].name);
quickSort(mm,0,N-1);
for (int i = 0 ; i < N ; i ++)//很SB的冒泡排学号
{
for(int j = i+1 ; j < N; j ++)
{
if (mm[i].score == mm[j].score)
{
int sss=strcmp(mm[i].name,mm[j].name);
if(sss != -1)
swap(mm[i],mm[j]);
}
}
}
cout <<ans<<endl;
for (int i = 0 ;i < N ;i ++)
if(mm[i].score>=G)
cout <<mm[i].name<<" "<<mm[i].score<<endl;
else
break;
}
}
1006
HDU 1280
第一种想法被cha掉了=。=
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cmath>
#include <cstring>
#include <queue>
#include <set>
#include <vector>
#include <stack>
#include <map>
#include <iomanip>
#define PI acos(-1.0)
#define Max 2005
#define inf 1<<28
#define LL(x) (x<<1)
#define RR(x) (x<<1|1)
#define FOR(i,s,t) for(int i=(s);i<=(t);++i)
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
#define mp(a,b) make_pair(a,b)
using namespace std;
int a[10000];
int b[10005];
int main()
{
int n , m;
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(b,0,sizeof(b));
for (int i = 0 ;i < n ; i ++)scanf("%d",&a[i]);
for (int i = 0 ;i < n ; i ++)
for (int j = i + 1 ; j < n ; j ++)b[a[i]+a[j]] ++;
int num = 0 ;
bool flag = 0 ;
for (int i = 10000 ; i >= 0; i --)
{
if(b[i])
{
for (int j = 0 ;j < b[i] ; j ++)
{
if( num == m - 1 )
{
printf("%d",i);
flag = 1;
break;
}
else
{
printf("%d ",i);
num ++;
}
}
}
if(flag)
break;
}
puts("");
}
return 0;
}
1007
HDU 1391 大水题。
1008
HDU 2673 大水题。