[NOIP2006] [动态规划] 金明的预算方案(提高组)

-v- 话说,虽然NOIP拿了一等奖,但至今还没把联赛题做一遍(普及组还有几个一直没做)。

最近想补补动态规划、递推之类的,先拿些简单的背包练练。

 

金明的预算方案(提高组),题目就不贴了。不算难的背包问题,就是在基本的0/1背包上加了一个简单的主/附件依赖。由于依赖关系很简单,每个主件最多被两个附件依赖,动规转移时用几个IF语句找下列情况最优的一个即可:

1.不选该主件及附件  2.只选主件  3.选主件和第一个附件  4.选主件和第二个附件  5.选主件和两个附件

 

以下是C++代码。直接用了背包的降维,动规中的那写IF语句为了减少重复运算做了优化,可能有点难看。

#include <fstream>
#include <cstring>

using namespace std;

int main ()
{
	ifstream fin("budget.in");
	ofstream fout("budget.out");

	int N, M;
	fin >> N >> M;
	int V[M][3], P[M][3];

	memset(V, 0, sizeof(V));
	memset(P, 0, sizeof(P));
	for (int i=0; i<M; i++)
	{
		int v, p, q;
		fin >> v >> p >> q;
		if (q)
			if (!V[q-1][1])
				V[q-1][1] = v,
				P[q-1][1] = p;
			else
				V[q-1][2] = v,
				P[q-1][2] = p;
		else
			V[i][0] = v,
			P[i][0] = p;
	}

	int f[N+1];
	memset(f, 0, sizeof(f));
	for (int i=0; i<M; i++)
		if (V[i][0])
			for (int j=N; j>=V[i][0]; j--)
			{
#define T(X) (V[i][X]*P[i][X])
				int rest = j-V[i][0], sum = T(0);

				if (f[rest] + sum > f[j])
					f[j] = f[j-V[i][0]] + sum;

				if (rest-V[i][1] >= 0  &&
						f[rest-V[i][1]] + sum + T(1) > f[j])
					f[j] = f[rest-V[i][1]] + sum + T(1);

				if ((rest = rest-V[i][2]) >= 0)
				{
					if (f[rest] + (sum = sum + T(2)) > f[j])
						f[j] = f[rest] + sum;

					if ((rest = rest-V[i][1]) >= 0)
						if (f[rest] + (sum = sum + T(1)) > f[j])
							f[j] = f[rest] + sum;
				}
			}

	int ans = 0;
	for (int i=1; i<=N; i++)
		if (f[i] > ans)
			ans = f[i];

	fout << ans << endl;

	fin.close();
	fout.close();

	return 0;
}

Tags: C/C++  联赛  NOIP  动态规划  

  • Digg
  • Del.icio.us
  • StumbleUpon
  • Reddit
  • Twitter
  • RSS

牛顿法解方程

关于牛顿法,参见:喂鸡百科

以下是我的程序。不连续的、定义域不是R的函数不能解。简单的高次方程还是可以对付的。

#include <cstdio>
#include <cmath>

using namespace std;

//示例函数
long double exfunc (const long double a)
{
	return a*a - 100;
}

//求函数func在x处的导数
inline long double EQ_Detivative (long double(*func)(const long double),
		const long double x)
{
	long double off = x / 10000.0;
	return (func(x + off) - func(x - off)) / (2.0 * off);
}

//解方程
long double EQ_Solve (long double(*func)(const long double),
		long double x, const long double off)
{
	long double k, y = func(x);

	while (abs(y) > off)
	{
		k = EQ_Detivative(func, x);
		x = x - (y / k);
		y = func(x);
	}

	return x;
}

int main ()
{
	//解方程exfunc(x)=0, 搜索起始点x=0.01, 精确度0.001
	printf("%Lf\n", EQ_Solve(exfunc, 0.01, 0.001));

	return 0;
}

Tags: 算法  数学  C/C++  

  • Digg
  • Del.icio.us
  • StumbleUpon
  • Reddit
  • Twitter
  • RSS

死亡人数最多的十大自然灾害

日本发生了8.8级的强烈地震,祝他们好运。

 

从维基百科上看到的东西,历史上有记载的死亡人数最多的十大自然灾害

1. 1931中国江淮大水:100万~250万

2. 1887中国黄河大水:90万人~200万

3. 1556中国陕西嘉靖大地震:83万

4. 1970孟加拉Bhola风暴:50万

5. 2010海地大地震:31.6万

6. 1839印度风暴:30万

7. 526叙利亚Antioch地震:25万~30万

8. 1976中国唐山大地震:24万多(最多的传言达到66.5万人)

9. 1920中国海原大地震:23.4万

10. 2004印度洋海啸:23万多

中国占了一半,说什么好呢……

 

此外,看到一个我没听说过的、发生在河南的灾难:
河南“75·8”溃坝事件:1975年8月,台风尼娜造成强降雨,导致河南南部淮河流域60多座水库溃坝(哇靠,这质量……),导致严重水灾。因为处于文革,死亡人数不明确,最多的估计在20万人以上。是目前世界上破坏程度最大的水库溃坝灾难。

 

嗯,大家要对国产玩意儿的质量有清醒的认识……所以我想,地震的话,撑几把伞跳楼吧。

Tags: 灾难  

  • Digg
  • Del.icio.us
  • StumbleUpon
  • Reddit
  • Twitter
  • RSS

[最大流, Edmonds–Karp算法] shuttle 太空飞行计划

问题描述

W 教授正在为国家航天中心计划一系列的太空飞行。每次太空飞行可进行一系列商业性实验而获取利润。现已确定了一个可供选择的实验集合 E={E1,E2,…,Em},和进行这些实验需要使用的全部仪器的集合I={I1, I2,…In}。 实验 Ej需要用到的仪器是 I的子集。配置仪器Ik的费用为ck美元。实验Ej的赞助商已同意为该实验结果支付pj美元。W教授的任务是找出一个有效算法, 确定在一次太空飞行中要进行哪些实验并因此而配置哪些仪器才能使太空飞行的净收益最大。这里净收益是指进行实验所获得的全部收入与配置仪器的全部费用的差额。

数据输入

由文件input.txt提供输入数据。文件第 1行有 2 个正整数 m和 n。m是实验数,n是仪器数。接下来的 m 行,每行是一个实验的有关数据。第一个数赞助商同意支付该实验的费
用;接着是该实验需要用到的若干仪器的编号。最后一行的 n个数是配置每个仪器的费用。

结果输出

程序运行结束时,将最佳实验方案输出到文件 output.txt 中。第 1 行是实验编号;第 2行是仪器编号;最后一行是净收益。

样例

shuttle.in

2 3
10 1 2
25 2 3
5 6 7

shuttle.out

1 2
1 2 3
17

 

《算法导论》上思考题有这个,这类问题的专业说法是“最大权闭合图”,用网络流解决。

关键在于如何建立网络流模型,网上有很好的讲解: http://blog.imzzl.com/2010/01/113.html

至于如何求收益最大时进行的实验和需要的器材,搜索残余网络得到最小割,输出源点所在部分的结点就行了。

我用的Edmonds–Karp算法(即BFS版本的Ford–Fulkerson)求最大流,以下是C++代码,练习了STL:

 

#include <iostream>
#include <fstream>
#include <cstring>
#include <vector>
#include <queue>

using namespace std;

const int INFFLOW = 0x7FFFFFFF;

struct ST_PATH {
	int c, end;
};

vector<ST_PATH> *map;

void addpath (const int u, const int v, const int c)
{	//向邻接表添加容量为c的边(u,v), 以及退流用的反向边(v,u)
	ST_PATH newp;

	newp.end = v;
	newp.c = c;
	map[u].push_back(newp);

	newp.end = u;
	newp.c = 0;
	map[v].push_back(newp);
}

int main (void)
{
	ifstream fin("shuttle.in");
	ofstream fout("shuttle.out");

	//读取数据及初始化
	int M, N, total=0;
	fin >> M >> N;
	map = new vector<ST_PATH> [M+N+2];
	for (int i=1; i<=M; i++)
	{
		int tmp;
		fin >> tmp;
		total += tmp;
		addpath(0, i, tmp);

		while (fin.get() != '\n')
		{ //Windows会读出错, 可以改成'\t'或者用stringstream什么的
			fin >> tmp;
			addpath(i, M+tmp, INFFLOW);
		}
	}

#define T (M+N+1)
	for (int i=M+1; i<=M+N; i++)
	{
		int c;
		fin >> c;
		addpath(i, T, c);
	}

	//Edmonds–Karp算法部分
	queue<int> q;
	int pflow[T+1], father[T+1], flow[T+1][T+1], maxflow = 0;
	memset(flow, 0, sizeof(flow));
	while (1)
	{
		memset(pflow, 0, sizeof(pflow));
		pflow[0] = INFFLOW;
		q.push(0);

		while (!q.empty())
		{
			int curr = q.front();
			q.pop();
			for (vector<ST_PATH>::iterator pi = map[curr].begin();
					pi < map[curr].end();  pi++)
			{
				int fl;
				if (!pflow[pi->end]  &&
						(fl = pi->c - flow[curr][pi->end]) > 0)
				{
					pflow[pi->end] = pflow[curr] > fl  ?  fl : pflow[curr];
					father[pi->end] = curr;
					q.push(pi->end);
				}
			}
		}

		if (!pflow[T])
			break;

		maxflow += pflow[T];
		for (int i=T; i; i=father[i])
			flow[father[i]][i] += pflow[T],
			flow[i][father[i]] -= pflow[T];
	}
	
	//从残留网络获取最小割包含原点的部分
	bool boo[M+N+1];
	memset(boo, 0, sizeof(boo));
	q.push(0);
	while (!q.empty())
	{
		int curr = q.front();
		q.pop();
		for (vector<ST_PATH>::iterator pi = map[curr].begin();
				pi < map[curr].end();  pi++)
			if (!boo[pi->end]  &&  pi->c - flow[curr][pi->end] > 0)
			{
				boo[pi->end] = true;
				q.push(pi->end);
			}
	}
	
	//输出部分
	for (int i=1; i<=M; i++)
		if (boo[i])
			fout << i << ' ';
	fout << endl;

	for (int i=M+1; i<=M+N; i++)
		if (boo[i])
			fout << i-M << ' ';
	fout << endl;

	fout << total - maxflow << endl;

	fin.close();
	fout.close();

	return 0;
}

 

Tags: 算法  网络流  C/C++  STL  

  • Digg
  • Del.icio.us
  • StumbleUpon
  • Reddit
  • Twitter
  • RSS

[二分图匹配, 匈牙利算法] pilot 飞行员配对

我就不贴题了,参见: http://acm.nankai.edu.cn/p2121.html

不带任何变化的、赤裸裸的二分图匹配问题。通过添加源和汇,可以转换成网络流问题。

今天学习了更加针对二分图匹配问题的匈牙利算法,又写了一遍这道题。

这个算法,原理不怎么好懂。可以参考:百度百科 郭嘉宝神牛的博客 和各种网络资源。

匈牙利算法程序非常好写,背下来也蛮容易的:

#include <stdio.h>
#include <string.h>
#include <stdbool.h>

int map[101][101], mat[101];
bool b[101];

bool path (const int wh)
{
	int i;
	for (i=1; i<=map[wh][0]; i++)
#define VT map[wh][i]
		if (!b[VT])
		{
			b[VT] = true;
			if (mat[VT]==0 || path(mat[VT]))
			{
				mat[VT] = wh;
				mat[wh] = VT;
				return true;
			}
		}
	return false;
}

int main (void)
{
	FILE *fin = fopen("pilot.in", "r"),
		 *fout = fopen("pilot.out", "w");
	int M, N;

	fscanf(fin, "%d%d", &M, &N);
	while (1)
	{
		int a, b;
		fscanf(fin, "%d%d", &a, &b);
		if (a==-1)
			break;
		map[a][++map[a][0]] = b;
	}

	//匈牙利算法
	int i, match=0;
	for (i=1; i<=M; i++)
	{
		memset(b, 0, sizeof(bool)*(M+N));
		if (path(i))
			match++;
	}

	fprintf(fout, "%d\n", match);

	for (i=1; i<=M; i++)
		if (mat[i])
			fprintf(fout, "%d %d\n", i, mat[i]);

	fclose(fin);
	fclose(fout);

	return 0;
}

Tags: C/C++  算法  网络流  

  • Digg
  • Del.icio.us
  • StumbleUpon
  • Reddit
  • Twitter
  • RSS

[NOI2003] [Splay(伸展树)] EDITOR2003 文本编辑器

文本编辑器

时间限制 2000 ms
内存限制 128 MB

【问题描述】

很久很久以前,DOS3.x的程序员们开始对EDLIN感到厌倦。于是,人们开始纷纷改用自己写的文本编辑器……

多年之后,出于偶然的机会,小明找到了当时的一个编辑软件。进行了一些简单的测试后,小明惊奇地发现:那个软件每秒能够进行上万次编辑操作(当然, 你不能手工进行这样的测试)!于是,小明废寝忘食地想做一个同样的东西出来。你能帮助他吗?   为了明确任务目标,小明对“文本编辑器”做了一个抽象的定义:

文本:由0个或多个字符构成的序列。这些字符的ASCII码在闭区间[32, 126]内,也就是说,这些字符均为可见字符或空格。

光标:在一段文本中用于指示位置的标记,可以位于文本的第一个字符之前,文本的最后一个字符之后或文本的某两个相邻字符之间。

文本编辑器:为一个可以对一段文本和该文本中的一个光标进行如下六条操作的程序。如果这段文本为空,我们就说这个文本编辑器是空的。

操作名称

输入文件中的格式

功能

MOVE(k)

Move k

将光标移动到第k个字符之后,如果k=0,将光标移到文本第一个字符之前

INSERT(n, s)

Insert n

S

在光标后插入长度为n的字符串s,光标位置不变,n ³ 1

DELETE(n)

Delete n

删除光标后的n个字符,光标位置不变,n ³ 1

GET(n)

Get n

输出光标后的n个字符,光标位置不变,n ³ 1

PREV()

Prev

光标前移一个字符

NEXT()

Next

光标后移一个字符

比如从一个空的文本编辑器开始,依次执行操作INSERT(13, “Balanced□tree”),MOVE(2),DELETE(5),NEXT(),INSERT(7, “□editor”),MOVE(0),GET(15)后,会输出“Bad□editor□tree”,如下表所示:

操作

操作前的文本

操作后的结果

INSERT(13, “Balanced□tree”)

|

(只有光标,文本为空)

|Balanced□tree

MOVE(2)

|Balanced□tree

Ba|lanced□tree

DELETE(5)

Ba|lanced□tree

Ba|d□tree

NEXT()

Ba|d□tree

Bad|□tree

INSERT(7, “□editor”)

Bad|□tree

Bad|□editor□tree

MOVE(0)

Bad|□editor□tree

|Bad□editor□tree

GET(15)

|Bad□editor□tree

输出“Bad□editor□tree”

上表中,“|”表示光标,“□”表示空格。   你的任务是:

  • 建立一个空的文本编辑器。
  • 从输入文件中读入一些操作指令并执行。
  • 对所有执行过的GET操作,将指定的内容写入输出文件。

【输入文件】

输入文件的第一行是指令条数t,以下是需要执行的t个操作。其中:

为了使输入文件便于阅读,Insert操作的字符串中可能会插入一些回车符,请忽略掉它们(如果难以理解这句话,可以参考样例)。

除了回车符之外,输入文件的所有字符的ASCII码都在闭区间[32, 126]内。且行尾没有空格。   这里我们有如下假定:

  • MOVE操作不超过50000个,INSERT和DELETE操作的总个数不超过4000,PREV和NEXT操作的总个数不超过200000。
  • 所有INSERT插入的字符数之和不超过2M(1M=1024*1024),正确的输出文件长度不超过3M字节。
  • DELETE操作和GET操作执行时光标后必然有足够的字符。MOVE、PREV、NEXT操作不会把光标移动到非法位置。
  • 输入文件没有错误。

  对C++选手的提示:经测试,对最大的测试数据使用fstream进行输入有可能会比使用stdio慢约1秒,因此建议在可以的情况下使用后者。

【输出文件】

输出文件的每行依次对应输入文件中每条GET指令的输出。

【样例输入】

15
Insert 26
abcdefghijklmnop
qrstuv wxy
Move 15
Delete 11
Move 5
Insert 1
^
Next
Insert 1
_
Next
Next
Insert 4
.\/.
Get 4
Prev
Insert 1
^
Move 0
Get 22

【样例输出】

.\/.
abcde^_^f.\/.ghijklmno

题解

田神牛推荐用Splay(伸展树,一种排序二叉树)做这道题,于是看了看论文,学习了Splay。

关于Splay的基本知识,推荐阅读: The Magical Splay

 

针对本题说一些问题:

1. 本题用的“二叉排序树”,不需要key域,即排序的关键字,只需要数据域存储字符。如果硬要说key的话,就算是字符的顺序吧。插入时只要插到光标所在节点与其后继节点之间即可,排序二叉树操作不会破坏顺序。

2. Insert插入字符串操作,尽管可以一个一个字符插入,但把字符串转换为一颗子树插入会更有效率。把字符串转换为树并没有什么要求,我为了省事直接转换成一个链了。

3. 对于这道题,Splay在Delete操作上比其他平衡树要有优势。把光标所在节点伸展到跟,再把删除部分的下一个节点伸展到根的右子树,则要删除的所有节点就在根的右子树的左子树,直接截断即可(没free内存,这不是正确的编程态度 - -)。

4. 在文本起始处和末尾处各插入一个占位字符,会使编程方便些。

5. Get操作时,仿照删除操作,把光标所在节点伸展到跟,再把要获取部分的下一个节点伸展到根的右子树,则要获取的所有节点就在根的右子树的左子树,然后对该子树进行中序遍历输出字符。

 

以下是我的代码,由于Splay是单独写的,程序代码保留了大量封装残余 ^_^

最近写的代码都很长,不知道是好是坏……

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct ST_SP_NODE {
	int size; //子结点数
	char ch; //数据域
	struct ST_SP_NODE *p, *left, *right;
} SP_node; //伸展树结点, 对于本题, 不需要key域.

typedef struct ST_SPTREE {
	struct ST_SP_NODE *NIL, *root; //NIL:哨兵 root:根结点
} SP_tree; //伸展树

FILE *fin, *fout; //全局变量, 输入输出文件

/*
 * < ISLSON 判断X是否为父节点的左孩子 >
 * < ISRSON 判断X是否为父节点的右孩子 >
 */
#define ISLSON(X) ((X)==(X)->p->left)
#define ISRSON(X) ((X)==(X)->p->right)

/* < SP_init 初始化树 > */
void SP_init (SP_tree *tree)
{
	tree->NIL = malloc(sizeof(SP_tree));
	tree->root = tree->NIL->p = tree->NIL;
	tree->NIL->size = 0;
}

/* < SP_R_rotate 右旋 > */
void SP_R_rotate (SP_tree *tree, SP_node *node)
{
	SP_node *l = node->left;

	l->p = node->p;
	if (node == tree->root)
		tree->root = l;
	else if (ISLSON(node))
		node->p->left = l;
	else
		node->p->right = l;

	node->p = l;
	node->left = l->right;
	l->right->p = node;
	l->right = node;

	l->size = node->size;
	node->size = node->left->size + node->right->size + 1;
}

/* < SP_L_rotate 左旋 > */
void SP_L_rotate (SP_tree *tree, SP_node *node)
{
	SP_node *r = node->right;

	r->p = node->p;
	if (node == tree->root)
		tree->root = r;
	else if (ISLSON(node))
		node->p->left = r;
	else
		node->p->right = r;

	node->p = r;
	node->right = r->left;
	r->left->p = node;
	r->left = node;

	r->size = node->size;
	node->size = node->left->size + node->right->size + 1;
}

/*
 * < SP_splay 树的伸展 >
 * 把结点node伸展为其祖先结点dest的子节点.
 * 如果dest==NIL, 伸展到根.
 */
void SP_splay (SP_tree *tree, SP_node *node, SP_node *dest)
{
	while (node->p != dest)
	{
		if (node->p->p == dest)
		{
			if (ISLSON(node))
				SP_R_rotate(tree, node->p);
			else
				SP_L_rotate(tree, node->p);
		}
		else if (ISLSON(node))
		{
			if (ISLSON(node->p))
			{
				SP_R_rotate(tree, node->p->p);
				SP_R_rotate(tree, node->p);
			}
			else
			{
				SP_R_rotate(tree, node->p);
				SP_L_rotate(tree, node->p);
			}
		}
		else
		{
			if (ISRSON(node->p))
			{
				SP_L_rotate(tree, node->p->p);
				SP_L_rotate(tree, node->p);
			}
			else
			{
				SP_L_rotate(tree, node->p);
				SP_R_rotate(tree, node->p);
			}
		}
	}
}

/* < SP_prevnode 求前驱结点 > */
SP_node *SP_prevnode (const SP_node *node)
{
	SP_node *pi = node->left;
	while (pi->size && pi->right->size)
		pi = pi->right;

	return pi;
}

/* < SP_succnode 求后继结点 > */
SP_node *SP_succnode (const SP_node *node)
{
	SP_node *pi = node->right;
	while (pi->size && pi->left->size)
		pi = pi->left;

	return pi;
}

/*
 * < SP_inserttree 插入子树 >
 * 将结点树为l的以node为根的子树插入到curr结点后继处.
 * 如果curr==NULL, 直接把tree的根设为该子树.
 */
void SP_inserttree (SP_tree *tree, SP_node *curr,
					SP_node *node, const int l)
{
	if (!curr)
	{
		tree->root = node;
		tree->root->p = tree->NIL;
		return;
	}

	SP_splay(tree, curr, tree->NIL);
	SP_node *pi = tree->root->right, *pj = tree->root;

	tree->root->size += l;
	while (pi->size)
	{
		pj = pi;
		pi->size += l;
		pi = pi->left;
	}

	node->p = pj;
	if (pj != tree->root)
		pj->left = node;
	else
		pj->right = node;
}

/*
 * < SP_freetree 释放子树内存 >
 * 释放子树node和其所有子结点的内存, 用于删除子树后的内存释放.
 * 对于本题, 为了提高速度, 并没有递归释放所有废弃内存.
 */
void SP_freetree (SP_tree *tree, SP_node *node)
{
	if (node == tree->root)
		tree->root = tree->NIL;
	else
	{
		if (ISLSON(node))
			node->p->left = tree->NIL;
		else
			node->p->right = tree->NIL;

		SP_node *pi;
		for (pi=node->p; pi!=tree->NIL; pi=pi->p)
			pi->size = pi->left->size + pi->right->size +1;
	}

	free(node);
}

/*
 * < SP_deleteall 删除连续多个结点 >
 * 删除序号在l和r的序号之间的所有结点.
 */
void SP_deleteall (SP_tree *tree, SP_node *l, SP_node *r)
{
	SP_splay(tree, l, tree->NIL);
	SP_node *pre = SP_prevnode(l);

	SP_splay(tree, r, tree->NIL);
	SP_node *suc = SP_succnode(r);

	if (pre != tree->NIL)
		SP_splay(tree, pre, tree->NIL);
	if (suc != tree->NIL)
		SP_splay(tree, suc, pre);

	if (suc != tree->NIL)
		SP_freetree(tree, suc->left);
	else if (pre != tree->NIL)
		SP_freetree(tree, pre->right);
	else
		SP_freetree(tree, tree->root);
}

/* < SP_find 查找第k个结点 > */
SP_node *SP_find (SP_node *node, const int k)
{
	if (node->left->size + 1 == k)
		return node;
	else if (node->left->size >= k)
		return SP_find(node->left, k);
	else
		return SP_find(node->right, k-(node->left->size+1));
}

/*
 * < str2spnode 字符串转换成树 >
 * 将长度为len的字符串按顺序转换为一个树, 并返回根.
 * 用于题中的Insert插入字符串操作.
 */
SP_node *str2spnode (const char *str, const int len, SP_node *NIL)
{
	SP_node *root = malloc(sizeof(SP_node)), *pi;
	int i;

	root->p = root->left = root->right = NIL;
	root->size = len;
	root->ch = str[0];

	pi = root;
	for (i=1; i<len; i++)
	{
		pi->right = malloc(sizeof(SP_node));
		pi->right->p = pi;
		pi = pi->right;

		pi->left = pi->right = NIL;
		pi->size = len-i;
		pi->ch = str[i];
	}

	return root;
}

/*
 * < TRV_inorder 中序遍历树 >
 * 输出以node为根的子树的中序遍历.
 * 用于题中的Get获取字符串操作.
 */
void TRV_inorder (const SP_node *node)
{
	if (node->left->size)
		TRV_inorder(node->left);

	fputc(node->ch, fout);

	if (node->right->size)
		TRV_inorder(node->right);
}

int main ()
{
	fin = fopen("editor2003.in", "r"),
	fout = fopen("editor2003.out", "w");

	int T, i;
	SP_tree text;
	SP_node *curr;

	fscanf(fin, "%d", &T);

	SP_init(&text);
	SP_inserttree (&text, NULL, str2spnode("{", 1, text.NIL), 1);
	curr = text.root;
	SP_inserttree (&text, text.root, str2spnode("}", 1, text.NIL), 1);

	for (i=0; i<T; i++)
	{
		char cmd[50];
		int arg;
		fscanf(fin, "%s", cmd);

		switch (cmd[0])
		{
			while (fgetc(fin) != '\n') continue;

			case 'M': // Move 移动光标
			{
				fscanf(fin, "%d", &arg);
				curr = SP_find(text.root, arg + 1);
				SP_splay(&text, curr, text.NIL);
				break;
			}

			case 'I': // Insert 插入字符串
			{
				fscanf(fin, "%d", &arg);
				char *arg2 = malloc((arg+1)*sizeof(char)), *pi, c;

				pi = arg2;
				while (pi!=arg2+arg)
					if ((c = fgetc(fin)) >= 32)
						*(pi++) = c;

				SP_inserttree (&text, curr,
						str2spnode(arg2, arg, text.NIL), arg);
				break;
			}

			case 'D': // Delete 删除字符串
			{
				fscanf(fin, "%d", &arg);
				SP_node *pend = SP_find(curr, curr->left->size + arg + 1);
				SP_deleteall(&text, SP_succnode(curr), pend);
				break;
			}

			case 'G': // Get 获取字符串
			{
				fscanf(fin, "%d", &arg);
				SP_node *pend = SP_find(text.root, curr->left->size + arg + 2);
				SP_splay(&text, curr, text.NIL);
				SP_splay(&text, pend, text.root);
				TRV_inorder(text.root->right->left);
				fputc('\n', fout);
				break;
			}

			case 'P': // Prev 光标前移
			{
				curr = SP_prevnode(curr);
				SP_splay(&text, curr, text.NIL);
				break;
			}

			case 'N': // Next 光标后移
			{
				curr = SP_succnode(curr);
				SP_splay(&text, curr, text.NIL);
				break;
			}
		}
	}

	fclose(fin);
	fclose(fout);

	return 0;
}

 
下面是我写的原版Splay(含有key域的普通排序二叉树)的插入节点代码,供参考:

void SP_insert (SP_tree *tree, const int key)
{
	SP_node *pi = tree->root, *pj = tree->NIL;

	while (pi != tree->NIL)
	{
		pi->size++;
		pj = pi;
		if (key > pi->key)
			pi = pi->right;
		else
			pi = pi->left;
	}

	SP_node *newnode = malloc(sizeof(SP_node));
	newnode->p = pj;
	newnode->left = newnode->right = tree->NIL;
	newnode->key = key;
	newnode->size = 1;

	if (pj == tree->NIL)
		tree->root = newnode;
	else if (key > pj->key)
		pj->right = newnode;
	else
		pj->left = newnode;

	SP_splay(tree, newnode, tree->NIL);
}

Tags: C/C++  NOI  算法  二叉树  

  • Digg
  • Del.icio.us
  • StumbleUpon
  • Reddit
  • Twitter
  • RSS

[二项堆, 并查集] Monkey King 猴子选大王

Monkey King 猴子选大王

【问题描述】
在一个森林里,住着N只好斗的猴子.开始,他们各自为政,互不相干.但是猴子们不能消除争吵,但这仅仅发生在两只互不认识的猴子之间.当争吵发生时,争吵的两只猴子都会求助他们各自最强壮的朋友,并且决斗.当然,决斗之后,两只猴子及他们所有的朋友都相互认识了,并且成为朋友,争吵将不会在他们之间发生.
假定每一只猴子有一个强壮值,在每次决斗之后变为原来的一半(例如,10将为变为5,5将会变为2).
假定每一只猴子认识他自己. 也就是当他发生争吵,并且自己是他的朋友中最强壮的,他将代表自己进行决斗.
 
【输入格式】
有几组测试数据,每组测试数据由两部分构成.
第一部分:第一行有一个整数 N(N<=100,000),表示猴子的数量.下面有N行.每行有一个数,表示猴子的强壮值(<=32768).
第二部分:第一行有一个整数M(M<=100,000),表示有M次争吵发生.下面有M行,每行有两个整数x和y,表示在第x只猴子和第y只猴子之间发生争吵.
 
【输出格式】
对于每一次争吵,如果两只猴子认识,输出-1,否则输出一个数,表示决斗后朋友中最强壮猴子的强壮值.

【输入输出样例】

monkeyk.in

5
20
16
10
10
4
5
2 3
3 4
3 5
4 5
1 5

monkeyk.out

8
5
5
-1
10

 

上个学期讲了二项堆,老师把这道题作为练习题。因为写起来比较复杂,一直没写。今天抽空搞定了。

 

因为要从猴子中不断选取最强壮的猴子,用堆记录体力值比较好。猴子认识后,还要进行堆合并操作,所以选择可合并堆数据结构——二项堆处理这道题比较合适。关于二项堆,参见维基百科条目或者自己用各种搜索引擎查询,还可以参见《算法导论》相关章节,这部分能看懂的。

 

对于本题,显然应该用大头堆。减小节点key后的维护是向下移动的,比向上移动要麻烦且费时一些:每次移动从该节点子女中选出key最大的一个,与该节点key交换,一直下移key直到它小于所有子女。

 

另外,由于要判断每个猴子属于哪一个猴群,或者说所在的二项堆,我还用了并查集作为索引,并用了路径压缩优化。

 

下面是C语言代码。因为是第一次写二项堆,一些地方实现的不是太好,不过足够过这道题目了(代码太长,默认折叠了)。

#include <stdio.h>
#include <stdlib.h>

/* 二项堆 */
struct ST_BH_HEAP;
struct ST_BH_NODE;

typedef struct ST_BH_NODE {
	int key, degree; //key:关键字  degree:下一层子女个数
	struct ST_BH_NODE *p, *son, *bro;
	//p:父节点  son:最左子节点  bro:右兄弟节点
} binheap_node; //二项堆节点(二项树)

typedef struct ST_BH_HEAP {
	struct ST_BH_NODE *head, *max, *tail;
	//head指向头节点, tail指向尾节点, max指向key最大的节点.
} binheap; //二项堆

/* 并查集 */
typedef struct ST_BCJ_NODE {
	struct ST_BH_HEAP *heap;
	struct ST_BCJ_NODE *p;
} bcj_node;

/* 全局变量 */
binheap_node *mk; //存储二项堆的节点, 不与猴子编号对应
bcj_node *idx; //并查集的节点, idx[n]是第(n+1)只猴子的并查集元素

/* 
 * < bh_node_init 二项树节点初始化 >
 * 初始化二项树node, 关键字设为key.
 * 返回值是为该节点分配的二项堆.
 */
binheap *bh_node_init (binheap_node *node, const int key)
{
	node->degree = 0;
	node->key = key;
	node->p = node->son = node->bro = NULL;
	binheap *belong = malloc(sizeof(binheap)); //开始时, 为每个二项树节点分配一个新堆
	belong->head = node;
	belong->max = node;
	belong->tail = node;

	return belong;
}

/* 
 * < bh_heap_resetmax 维护堆max指针 >
 * 减小节点关键字后可能破坏二项堆max指针, 通过该操作重新设置max指针.
 */
void bh_heap_resetmax (binheap *heap)
{
	binheap_node *pi, *pmax = heap->head;
	
	for (pi=pmax->bro; pi; pi=pi->bro)
		if (pi->key > pmax->key)
			pmax = pi;

	heap->max = pmax;
}

/* 
 * < bh_node_dec 减小一个节点的关键字 >
 * 减小node的关键字到nkey.
 */
void bh_node_dec (binheap_node *node, const int nkey)
{
	node->key = nkey;
	while (node->son)
	{
		binheap_node *pi, *pmax;

		pmax = node->son;
		for (pi=node->son->bro; pi; pi=pi->bro)
			if (pi->key > pmax->key)
				pmax = pi;

		if (node->key < pmax->key)
		{
			int tmp = node->key;
			node->key = pmax->key;
			pmax->key = tmp;
			node = pmax;
		}
		else
			break;
	}
}

/* 
 * < bh_heap_linknode 链接二项树与二项堆 >
 * 把以node为根的二项树链接在binheap的末尾.
 */
void bh_heap_linknode (binheap *h, binheap_node *node)
{
	if (h->tail)
	{
		h->tail->bro = node;
		node->bro = NULL;
		h->tail = node;
		if (node->key > h->max->key)
			h->max = node;
	}
	else
	{
		h->head = h->tail = h->max = node;
		node->bro = NULL;
	}
}

/* 
 * < bh_node_union 合并二项树 >
 * 合并二项树na和nb, 返回合并后的新二项树。
 */
binheap_node *bh_node_union (binheap_node *na, binheap_node *nb)
{
	if (na->key < nb->key)
	{
		binheap_node *tmp = na;
		na = nb;
		nb = tmp;
	}

	nb->bro = na->son;
	nb->p = na;
	na->son = nb;
	na->bro = NULL;
	na->degree++;

	return na;
}

/* 
 * < bh_heap_deltail 删除二项堆末端的二项树 >
 * 从二项堆h中删除h->tail指向的二项树, 并完成指针链接的维护.
 * 后面合并堆操作用到的, 不是标准的二项堆操作.
 */
void bh_heap_deltail (binheap *h)
{
	if (h->tail == h->head)
		h->head = h->tail = h->max = NULL;
	else
	{
		binheap_node *pi = h->head, *pmax = h->head;

		while (pi->bro != h->tail)
		{
			pi = pi->bro;
			if (pi->key > pmax->key)
				pmax = pi;
		}

		pi->bro = NULL;
		h->tail = pi;
		h->max = pmax;
	}
}

/* 
 * < bh_heap_union 合并二项堆 >
 * 合并二项堆ha和hb, 并返回合并后的堆.
 * 自己想的方法, 和一般的实现不太一样, 不如一般的实现好.
 */
binheap *bh_heap_union (binheap *ha, binheap *hb)
{
	binheap *h = malloc(sizeof(binheap));
	h->head = h->tail = h->max = NULL;

	binheap_node *pa = ha->head,
				 *pb = hb->head;

	while (pa || pb)
	{
		binheap_node *add;

		if (pa && (!pb || pa->degree < pb->degree))
		{
			add = pa;
			pa = pa->bro;
		}
		else if (!pa || pa->degree > pb->degree)
		{
			add = pb;
			pb = pb->bro;
		}
		else
		{
			binheap_node *nexta = pa->bro, *nextb = pb->bro;
			add = bh_node_union(pa, pb);
			pa = nexta;
			pb = nextb;
		}

		if (!h->tail  ||  add->degree != h->tail->degree)
			bh_heap_linknode(h, add);
		else
		{
			add = bh_node_union(add, h->tail);
			bh_heap_deltail(h);
			bh_heap_linknode(h, add);
		}
	}

	free(ha);
	free(hb);

	return h;
}

/* 
 * < bcj_node_init 并查集元素初始化 >
 * 初始化元素node, 单独成一个集合, 指向的二项堆设为heap.
 */
void bcj_node_init (bcj_node *node, binheap *heap)
{
	node->p = NULL;
	node->heap = heap;
}

/* 
 * < bcj_getpa 查找元素最远祖先 >
 * 递归查找a的最远祖先, 并进行并查集的路径压缩优化.
 */
bcj_node *bcj_getpa (bcj_node *a)
{
	if (!a->p)
		return a;
	else
		return (a->p = bcj_getpa(a->p));
}

/* 
 * < bcj_union 集合合并 >
 * 合并元素a和b所在的并查集集合.
 */
void bcj_union (bcj_node *a, bcj_node *b)
{
	bcj_node *fa = bcj_getpa(a),
			 *fb = bcj_getpa(b);
	fb->p = fa;
}

/* 
 * < mk_fight 猴子对战 >
 * 模拟猴子(a+1)和猴子(b+1)的对战, 进行二项堆操作.
 * 返回题目要求输出的强壮值或者-1.
 */
int mk_fight (const int a, const int b)
{
	bcj_node *fa = bcj_getpa(idx+a),
			 *fb = bcj_getpa(idx+b);

	if (fa->heap == fb->heap)
		return -1;
	else
	{
		binheap *ha = fa->heap,
		        *hb = fb->heap;

		bh_node_dec(ha->max, (ha->max->key)>>1);
		bh_heap_resetmax(ha);
		bh_node_dec(hb->max, (hb->max->key)>>1);
		bh_heap_resetmax(hb);

		binheap *newheap = bh_heap_union(ha, hb);

		bcj_union(fa, fb);
		fa->heap = newheap;

		return newheap->max->key;
	}
}

/* 主程序 */
int main (void)
{
	FILE *fin = fopen("monkeyk.in", "r"),
	     *fout = fopen("monkeyk.out", "w");
	int n, m, i;

	fscanf(fin, "%d", &n);
	mk = malloc(n*sizeof(binheap_node));
	idx = malloc(n*sizeof(bcj_node));
	for (i=0; i<n; i++)
	{
		int str;
		fscanf(fin, "%d", &str);
		bcj_node_init(idx+i, bh_node_init(mk+i, str));
	}

	fscanf(fin, "%d", &m);
	for (i=0; i<m; i++)
	{
		int a, b;
		fscanf(fin, "%d %d", &a, &b);
		fprintf(fout, "%d\n", mk_fight(a-1, b-1));
	}

	fclose(fin);
	fclose(fout);

	return 0;
}

 

Tags: 二项堆  算法  并查集  C/C++  

  • Digg
  • Del.icio.us
  • StumbleUpon
  • Reddit
  • Twitter
  • RSS

归并排序,也是很快的!

归并排序

两个已排序数组的归并操作:每次从两个数组头部取出最小(或最大)的一个元素,出队,加入新数组末端,直到取完,得到由两个已排序数组元素组成的一个新的已排序数组。

 

把一个未排序数组不断分割,看成许多单元素组成的数组(可以作为有序的),然后不断归并,最后得到一个排序过的数组。这就是归并排序

 

O(nlog(n))的时间复杂度,和快排、堆排一样。不过归并排序总是需要额外的空间存储新数组,所以不如快排的。其优点是没有特别糟的情况,最坏时间复杂度还是那么多。  

 

用C语言写了个递归实现的,gcc 4.5.2,归并排序效率和标准库的qsort()差不多,略快一些(20000000随机数:qsort() 4.06s  归并排序 3.98s),还是相当不错的排序算法。感觉递归可以消去,可能再快一些。  

 

void MergeSort (int *a, const int len)
{
	if (len > 2)
	{
		MergeSort (a, len/2);
		MergeSort (a+len/2, len-len/2);

		int *buf = malloc( len * sizeof(int) );
		int i, *p1 = a, *p2 = a+len/2;
		for (i=0;  i<len-1 && p1!=a+len/2 && p2!=a+len;  i++)
			if (*p1 < *p2)
				buf[i] = *p1++;
			else
				buf[i] = *p2++;

		if (p1 != a+len/2)
			memcpy(buf+i, p1, (len-i)*sizeof(int));
		else if (p2 != a+len)
			memcpy(buf+i, p2, (len-i)*sizeof(int));

		memcpy(a, buf, len*sizeof(int));
		free(buf);
	}
	else if (len == 2 && a[0] > a[1])
	{
		int temp = a[0];
		a[0] = a[1];
		a[1] = temp;
	}
}

Tags: 算法  排序  C/C++  

  • Digg
  • Del.icio.us
  • StumbleUpon
  • Reddit
  • Twitter
  • RSS

另一个冒泡排序优化——鸡尾酒排序

From: [Wikipedia 鸡尾酒排序] :

鸡尾酒排序,也就是定向冒泡排序, 鸡尾酒搅拌排序, 搅拌排序 (也可以视作选择排序的一种变形), 涟漪排序, 来回排序 or 快乐小时排序, 是冒泡排序的一种变形。此算法与冒泡排序的不同处在于排序时是以双向在序列中进行排序。

 

原理类似冒泡排序。一次循环中,先从前往后把大数冒上去,再从后往前把小数冒回来。比起基本的冒泡排序,从两端缩小未排序范围更有效率。

 

排序50000个随机数,与上次写的冒泡排序的比较:冒泡排序---5.79s    鸡尾酒排序---4.05s

 

以下是Pascal代码,和上次写的冒泡排序用一样的优化。为了简洁,只给出排序函数,swaplnt是交换变量函数,省略了。用法:cocktailsort (长整形数组, 待排序区域下界, 上界)。

 

procedure cocktailsort (var a: array of longint;  down, up: longint);
	var
		j, lastswp: longint;
		swapped: boolean;
	begin
		repeat
			swapped := false;

			lastswp := 0;
			for j := down to up-1 do
				if a[j] > a[j+1] then
				begin
					swaplint(a[j], a[j+1]);
					lastswp := j;
				end;

			if lastswp <> 0 then
			begin
				up := lastswp;
				swapped := true;
			end;

			lastswp := 0;
			for j := up downto down+1 do
				if a[j] < a[j-1] then
				begin
					swaplint(a[j], a[j-1]);
					lastswp := j;
				end;

			if lastswp <> 0 then
			begin
				down := lastswp;
				swapped := true;
			end;
		until not(swapped);
	end;

 

 

Tags: Pascal  算法  

  • Digg
  • Del.icio.us
  • StumbleUpon
  • Reddit
  • Twitter
  • RSS

优化的快速排序

按照书上的最优快速排序代码,写了两份简单的。驱动程序读取数据规模n,随机数填充一个长度为n的数组,从小到大排序,并输出排序时间。

@ Athlon 64 X2 5200+ 2.75GHz    /    2x2GB DDR2 800

@ Archlinux ---- 2.6.37-pf x86-64 内核

GCC 4.5.2:gcc source.c -O3 -std=c99

FPC 2.4.2:fpc source.pas -O3  

 

C99实现(修改COMP改变排序顺序; 用标准库qsort比较时间):

#include <stdio.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <sys/time.h>

// 调试开关
//#define DEBUGPRINT

/* 交换变量的宏函数 */
#define SWAP(X,Y) {temp=(X);(X)=(Y);(Y)=temp;}
/* 获取时间差的宏函数 */
#define TIMEDIFF(X,Y) (1000000*((Y).tv_sec-(X).tv_sec)+(Y).tv_usec-(X).tv_usec)

/* 用于自编快排的比较函数 */
extern inline int COMP (const int x, const int y)
{
	return x > y ? 1 :
			(x < y ? -1 : 0);
}

/* 用于标准qsort()的比较函数 */
int COMP2 (const void *x, const void *y)
{
	return *(int*)x - *(int*)y;
}

/* 自编快排 --- 快速排序部分 */
void xquicksort (int *pL, int *pR)
{
	ptrdiff_t diff;
	int mid, temp, *pi, *pj, *pmid, ppivot;

	while (( diff = pR - pL ) >= 36 )
	//36可修改为大于3的任何数
	//越大快排作用越小,同时插排作用越大
	{
		mid = rand() % (diff-2) + 1;
		pmid = pL + mid;

		if (COMP(*pL, *pmid)>0)
			SWAP(*pL, *pmid);
		if (COMP(*pL, *pR)>0)
			SWAP(*pL, *pR);
		if (COMP(*pmid, *pR)>0)
			SWAP(*pmid, *pR);

		SWAP(*pmid, *(pR-1));

		pi = pL;
		pj = pR - 1;
		ppivot = *pj;
		while (1)
		{
			while (COMP(*++pi, ppivot) < 0)
				continue;
			while (COMP(*--pj, ppivot) > 0)
				continue;

			if (pi>=pj)
				break;
			else
				SWAP(*pi, *pj);
		}

		*(pR - 1) = *pi;
		*pi = ppivot;

		if (pi - pL > pR - pi)
		{
			xquicksort(pi+1, pR);
			pR = pi - 1;
		}
		else
		{
			xquicksort(pL, pi-1);
			pL = pi + 1;
		}
	}
}

/* 自编快排 --- 插入排序部分 */
void InsSort (int *arr, const int len)
{
	int i, j, temp;
	for (i = 1;  i < len;  i++)
	{
		temp = arr[i];
		for (j = i - 1;  j >= 0;  j--)
			if (COMP(arr[j], temp) > 0)
				arr[j+1] = arr[j];
			else
				break;

		arr[j+1] = temp;
	}
}

/* 自编快排 --- 用户接口部分 */
void QSort (int *arr, const int len)
{
	xquicksort(arr, arr+len-1);
	InsSort(arr, len);
}

int main (void)
{
	int n, *arr, *arr2, i;
	struct timeval stime, etime;

	printf("数据规模: ");
	scanf("%d", &n);

	arr = malloc(n * sizeof(int));
	arr2 = malloc(n * sizeof(int));
	srand((int)arr);

	puts("数组填充中...");
	for (i = 0;  i < n;  i++)
	{
		arr2[i] = arr[i] = rand();
#ifdef DEBUGPRINT
		printf("%d ", arr[i]);
#endif
	}

	puts("\n开始使用自编函数排序...");
	gettimeofday(&stime, NULL);
	QSort(arr, n);
	gettimeofday(&etime, NULL);
	printf("排序用时: %.3f ms\n", (float)TIMEDIFF(stime, etime) / 1000.0);

	puts("\n开始使用标准库函数排序...");
	gettimeofday(&stime, NULL);
	qsort(arr2, n, sizeof(int), COMP2);
	gettimeofday(&etime, NULL);
	printf("排序用时: %.3f ms\n", (float)TIMEDIFF(stime, etime) / 1000.0);

#ifdef DEBUGPRINT
	bool wrong = false;
	puts("排序后数组:");
	for (i = 0;  i < n-1;  i++)
	{
		if (arr[i] != arr2[i])
			wrong = true;
		printf("%d ", arr[i]);
	}
	puts(wrong ? "\n算法错误" : "\n正常结束");
#endif

	return 0;
}

  Pascal实现(修改所有comp注释行,反转不等号,即可调整排序顺序; 用FP示例快排比对时间):

program QuickSort_im;

uses
	dos;

var
	n, i, stime, etime : longint;
	wrong : boolean;
	arr, arr2 : array of longint;

(*
 * < gettimeofday_hs: 获取时间 >
 * 返回值: 以0.01秒为单位的当日时间
 * 本程序中用于统计排序用时。
 *)
function gettimeofday_hs : longint;
	var
		hour, minute, second, sec100 : word;
	begin
		gettime(hour, minute, second, sec100);
		exit( hour*360000 + minute*6000 + second*100 + sec100 );
	end;

// ------------ 快速排序所需函数 BEGIN ------------ //
(*
 * < swap: 交换变量 >
 * swap (变量a, 变量b)
 *)
procedure swap (var a, b: longint);
	var
		temp: longint;
	begin
		temp := a;
		a := b;
		b := temp;
	end;

(*
 * < inssort: 插入排序 >
 * inssort (待排序数组, 排序区域下界, 排序区域上界)
 * 本程序中用于快排后期处理。
 *)
procedure inssort (var a: array of longint; const down, up: longint);
	var
		i, j, add, temp : longint;
	begin
		for i := down+1 to up do
		begin
			temp := a[i];

			add := down;
			for j := i-1 downto down do
				if a[j] > temp then //COMP
					arr[j+1] := arr[j]
				else
				begin
					add := j+1;
					break;
				end;

			arr[add] := temp;
		end;
	end; // inssort END

(*
 * < qsort: 自己写的快速排序 >
 * qsort (待排序数组, 排序区域下界, 排序区域上界)
 * 按照书上的方法,优化过的快排,从C代码移植。
 *)
procedure qsort (var a: array of longint;  const down, up: longint);

	procedure xquicksort (l, r: longint);
		var
			diff, i, j, pivot: longint;
		begin
			diff := r - l;

			while diff >= 24 do
			//24可修改为大于3的任何数
			//越大快排作用越小,同时插排作用越大
			begin
				i := random(diff-2) + 1 + l;
				if a[l] > a[i] then //COMP
					swap(a[l], a[i]);
				if a[l] > a[r] then //COMP
					swap(a[l], a[r]);
				if a[i] > a[r] then //COMP
					swap(a[i], a[r]);
				swap(a[i], a[r-1]);

				i := l;
				j := r-1;
				pivot := a[j];
				repeat
					repeat inc(i); until a[i] >= pivot; //COMP
					repeat dec(j); until a[j] <= pivot; //COMP

					if i>=j then
						break
					else
						swap(a[i], a[j]);
				until false;

				a[r-1] := a[i];
				a[i] := pivot;

				if i-l > r-i then
				begin
					xquicksort(i+1, r);
					r := i-1;
				end
				else
				begin
					xquicksort(l, i-1);
					l := i + 1;
				end;

				diff := r - l;
			end;
		end; // qsort->xquicksort END

	begin
		xquicksort(down, up);
		inssort(arr, down, up);
	end; // qsort END

(*
 * < qsort_fp: FreePascal 快速排序示例 >
 * qsort_fp (待排序数组, 排序区域下界, 排序区域上界)
 * FP安装路径/demo/text/qsort.pp 快速排序示例。
 *)

procedure qsort_fp (var a: array of longint;  const down, up: longint);
	procedure sort(l,r: longint);
		var
			i,j,x,y: longint;
		begin
			i:=l;
			j:=r;
			x:=a[(l+r) div 2];

			repeat
				while a[i]<x do inc(i);
				while x<a[j] do dec(j);

          			if i<=j then
				begin
					y:=a[i];
					a[i]:=a[j];
					a[j]:=y;
					inc(i);
					dec(j);
				end;
			until i>j;

			if l<j then sort(l,j);
			if i<r then sort(i,r);
		end; // qsort_fp->sort END

	begin
		sort(down, up);
	end; // qsort_fp END

// ------------ 快速排序所需函数 END ------------ //

begin
	write('数据规模: ');
	readln(n);	

	randomize;
	setlength(arr, n);
	setlength(arr2, n);
	writeln('数组填充中...');
	writeln;
	for i := 0 to n-1 do
	begin
		arr[i] := random(maxlongint);
		arr2[i] := arr[i];
	end;

	writeln('开始使用自编函数排序...');
	stime := gettimeofday_hs();
	qsort(arr, 0, n-1);
	etime := gettimeofday_hs();
	writeln('排序用时: ', (etime-stime)/100.0 :0:2, ' s');

	writeln;
	writeln('开始使用FreePascal示例函数排序...');
	stime := gettimeofday_hs();
	qsort_fp(arr2, 0, n-1);
	etime := gettimeofday_hs();
	writeln('排序用时: ', (etime-stime)/100.0 :0:2, ' s');

	// 检查排序结果
	wrong := false;
	for i := 0 to n-1 do
		if arr[i] <> arr2[i] then
			wrong := true;

	writeln;
	if wrong then
		writeln('算法错误.')
	else
		writeln('正常结束.');
	// 检查 END
end.

排序20000000个数的性能比较: C 实现:2.08 s C qsort():4.03 s Pascal 实现:3.36 s Pascal 示例:4.18 s

Tags: Pascal  C/C++  算法  

  • Digg
  • Del.icio.us
  • StumbleUpon
  • Reddit
  • Twitter
  • RSS