UVaOJ 11136 Hoax or what

3 篇文章 0 订阅
2 篇文章 0 订阅

题目链接:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=23&page=show_problem&problem=2077

题目要求不要用STL。所以需要自己实现priority_queue, STL的是用堆实现的,仿造之。。。例如最大堆可以实现top为最大值的priority_queue。最大堆的特点就是根节点永远比子节点值大,(左右节点谁比较大都无所谓)具体就是用一个数组,每次push一个数,先放在当前数组的最后面,(所以需要一个记录队列个数的e变量),然后通过一个upd操作把它换到合适的位置,因为根于子节点是x与2*x 或2 *x + 1的关系,所以知道e,即当前节点的数组下标可以知道它的根节点下标,如果子比父大,则swap两个的值,然后递归调用。而pop操作,则先把a[1]赋值为a[e], (于是a[1]就相当于没有了,注意a[1]是队列首位,而不能是a[0]), 然后需要把a[1]调整到合适位置,需要一个down操作,父亲如果左右儿子中的较大的那个小,则交换,然后递归调用。注意down和upd操作都有边界,分别是e和1,下标不能超过这两个。由于,swap,upd,down,只需要在这个类生成的时候调用,所以可以写成私有的内联函数,此外,我还写了个公有的show,方便调试,可以忽略。我实现的时候e是最后一个元素的下一个空位子的下标,和上面说的有点矛盾,将就看吧。第一次实现,刚学的类。。比较挫,不知道会不会过系统测试,现在貌似还不能提交(会SE),以后再交吧。

因为题目既要最大值也要最小值,所以写了两个,一个pq,一个mqp,即一个top是最大值,另一个是最小值。

代码如下:

头文件:

class pqueue{
		int *a, e;
		void swap(int &x, int &y){
			x = x + y;
			y = x - y;
			x = x -  y;
		}

		void down(int x){
			if (a[x] < a[2 * x] && 2 * x < e) {
				swap(a[x], a[2 * x]);
				if (e > 2 * x) down(2 * x);
			}
			if (a[x] < a[2 * x + 1] && 2 * x + 1 < e){
				swap(a[x], a[2 * x + 1]);
				if (e > 2 * x + 1) down(2 * x + 1);
			} 
		}

		void upd(int x){
			if (x > 1){
				if (a[x] > a[x >> 1]) 
				{
						swap(a[x], a[x >> 1]);
						if ((x >> 1) > 1) upd(x >> 1);
				}
			}
		}
public :
		void pop(void);
		void push(int x);
		void create(int x);
		int top(void);
		void clear(void);
		void show(void);
};
class mpqueue{
		int *a, e;
		void swap(int &x, int &y){
			x = x + y;
			y = x - y;
			x = x -  y;
		}

		void down(int x){
			if (a[x] > a[2 * x] && 2 * x < e) {
				swap(a[x], a[2 * x]);
				if (e > 2 * x) down(2 * x);
			}
			if (a[x] > a[2 * x + 1] && 2 * x + 1 < e){
				swap(a[x], a[2 * x + 1]);
				if (e > 2 * x + 1) down(2 * x + 1);
			} 
		}

		void upd(int x){
			if (x > 1){
				if (a[x] < a[x >> 1]) 
				{
						swap(a[x], a[x >> 1]);
						if ((x >> 1) > 1) upd(x >> 1);
				}
			}
		}
public :
		void pop(void);
		void push(int x);
		void create(int x);
		int top(void);
		void clear(void);
		void show(void);
};
函数实现:

#include"pqueue.h"
#include<iostream>
#include<cstring>
#include<cstdlib>

void pqueue :: pop(void){
		a[1] = a[--e];
		down(1);
}

void pqueue :: push(int x){
		a[e]  = x;
		upd(e++);
}

int pqueue :: top(void){
		return a[1];
}

void pqueue :: create(int x){
		a = (int *) malloc(4 * x * sizeof (int));
		memset(a, 0, sizeof(a) * 4 * x);
		e = 1;
}

void pqueue :: clear(void){
		free(a);
		a = NULL;
		e = 1;
}

void pqueue :: show(void){
		for (int i = 1; i < e; ++i) std :: cout << a[i] << ' ';
		std :: cout << std :: endl;
}
#include"mpqueue.h"
#include<iostream>
#include<cstring>
#include<cstdlib>

void mpqueue :: pop(void){
		a[1] = a[--e];
		down(1);
}

void mpqueue :: push(int x){
		a[e]  = x;
		upd(e++);
}

int mpqueue :: top(void){
		return a[1];
}

void mpqueue :: create(int x){
		a = (int *) malloc(4 * x * sizeof (int));
		memset(a, 0, sizeof(a) * 4 * x);
		e = 1;
}

void mpqueue :: clear(void){
		free(a);
		a = NULL;
		e = 1;
}

void mpqueue :: show(void){
		for (int i = 1; i < e; ++i) std :: cout << a[i] << ' ';
		std :: cout << std :: endl;
}
主程序:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include"pqueue.h"
#include"mpqueue.h"
#pragma warning (disable : 4996)
#define N 10000005
using namespace std;

pqueue a;
mpqueue b;

int main(){
		int n, x, i, j, k, tem, re;
		while (scanf("%d", &n), n){
				a.create(N), b.create(N);
				for (i = 0, re = 0; i < n; ++i){
						scanf("%d", &x);
						for (j = 0; j < x; ++j){
								scanf("%d", &tem);
								a.push(tem);   
								b.push(tem);
						}
						/*a.show(), b.show();*/
						re += a.top() - b.top();
						a.pop(), b.pop();
						/*a.show(), b.show();*/
				}
				a.clear(), b.clear();
				printf("%d\n", re);
		}
		return 0;
}
就是这样啦,其实重载运算符也可以把最大变成最小,但是mqp与pq代码其实一模一样,只改了两个小于号,(改成大于)所以就不用重载运算符了。

是不是写的太挫了?(最近刚学类,不要鄙视。。。)另外,STL中的pq是动态长度的,但是我不会动态变长度,所以只能用malloc开一个做够大的了。(其实一开始是想对每天malloc一下的,后来不知道该怎么把它们连起来就放弃了,直接开了10000005,不知道会不会RE,有没有其他好的方法,防止这种情况,比较简单的实现随时变长的功能?求指教)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值