光影切割问题之求解逆序数

1. 问题

      编程之美1.7光影切割问题可以概括为:

      设有两条完全相同的垂直方向上的两条长度相同的线段ab,且它们对应的端点在同一水平线上。

      已知:在这两条线段之间存在n条线段,且每条线段的起点都在线段a上,终点在线段b上。

      求:n条线段将线段ab构成的矩形平面分割成的块数B

2. 求解

      方法一:不难发现B = n + 1 + i,其中i表示图中直线交点的个数。因此该问题就转化为求直线交点个数的问题。

      方法二:更为巧妙,进一步将问题转化为求逆序数问题。但书中没有给出如何求解逆序数。

3. 求逆序数

3.1构造测试数据集

      以下为我用于产生测试数据的代码,共差生9999个随机整数。值得注意的是该数据集中存在相同的元素,在求解逆序数时应特别注意。产生的随机数将存储到文件dataSource.txt中。

<span style="font-size:14px;">#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <fstream>
using namespace std;

#define DATANUM 9999

int main()
{
	int i = 0;
	int temp;
	srand((unsigned)time(NULL));
	ofstream fin("dataSource.txt");
	for (i=0; i<DATANUM; ++i)
	{
		temp = rand()%99999;
		fin<<temp<<endl;
	}
	fin.close();
	return 0;
}
</span>


 

3.2问题分析

      以下部分纯属个人理解,如有不正确欢迎大家指正。

      一个数的逆序数为在它后边比它小的数据的个数。如有序列31,对数据3来说后边只有一个1比它小,因此它的逆序数为1;而对于1来说后边没有数据,则它的逆序数为0

      仔细分析后不难发现一个数串所有逆序数之和与排序时数据交换的次数相同(按照从小到大的顺序排列)。例如有序列321,用冒泡排序需要交换3次,明显与该序列的逆序数相等。

      实际上选择枢纽为2的快排,只需交换了一次。简单分析可得:快速排序没有进行13的比较,而逆序数需要考虑任何两个数字之间的关系。因此快速排序不可用,且不稳定的排序算法也是不可用的,因为他们会交换相同数字之间的位置。另外逆序数可以用分支法来求解,即归并算法。以下为用冒泡排序和归并排序求解逆序数的实现。

<span style="font-size:14px;">#include <iostream>
#include <fstream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;

#define MAXNUM 9999
#define MAXNUMlENGTH 10

void readData(int sourceData[MAXNUM]);
int bubbleSort(int sourceData[MAXNUM]);
int merge(int sourceData[MAXNUM], const int lStart, const int lEnd, const int rStart, const int rEnd);
int mergesort(int sourceData[MAXNUM], int begin, int end);
void output(int sourceData[MAXNUM]);

int main()
{
	int sourceData[MAXNUM];
	readData(sourceData);
	int choice = 0;
	int flag = 1;
	while (flag)
	{
		cout<<"Run BullSort, please input 0"<<endl;
		cout<<"Run MergeSort, please input 1"<<endl;
		cin>>choice;
		switch (choice)
		{
		case 0:
			flag = 0;
			cout<<"BullleSort result: ";
			cout<<bubbleSort(sourceData)<<endl;
			break;
		case 1:
			flag = 0;
			cout<<"Merge result: ";
			cout<<mergesort(sourceData, 0, MAXNUM-1)<<endl;
			break;
		default:
			break;
		}
	}
	return 0;
}

void readData(int sourceData[MAXNUM])
{
	ifstream fin("dataSource.txt");
	if (!fin)
	{
		cout<<"Can't Open the File:"<<endl;
		exit(1) ;
	}
	int i = 0;
	char tempc[MAXNUMlENGTH];
	while (fin>>tempc)
	{
		sourceData[i++] = atoi(tempc);
	}
	fin.close();
}

//ascending
int bubbleSort(int sourceData[MAXNUM])
{
	int exchangeTime = 0;
	int temp = 0;
	for (int i=0; i<MAXNUM; ++i)
	{
		for (int j=1; j<MAXNUM-i; ++j)
		{
			if (sourceData[j-1]>sourceData[j])
			{
				temp = sourceData[j-1];
				sourceData[j-1] = sourceData[j];
				sourceData[j] = temp;
				exchangeTime++;
			}
		}
	}
	return exchangeTime;
}

void output(int sourceData[MAXNUM])
{
	for (int i=0; i<MAXNUM; ++i)
	{
		cout<<sourceData[i]<<"  ";
		if (4 == i%5)
			cout<<endl;
	}
}


int merge(int sourceData[MAXNUM], const int lStart, const int lEnd, const int rStart, const int rEnd)
{
	int exchangeTime = 0;
	int tempData[MAXNUM];
	int templStart = lStart;
	int templEnd = lEnd;
	int temprStart = rStart;
	int temprEnd = rEnd;
	int i = lStart;

	while (templStart<=templEnd && temprStart<=temprEnd)
	{
		if (sourceData[templStart]<=sourceData[temprStart])
		{
			tempData[i++] = sourceData[templStart++];//没有交换
		}
		else
		{
			tempData[i++] = sourceData[temprStart++];//发生交换
			exchangeTime = exchangeTime + templEnd - templStart + 1;//注意理解
		}
	}

	for ( ; templStart<=templEnd; ++templStart)
	{
		tempData[i++] = sourceData[templStart];
	}
	for ( ; temprStart<=temprEnd; ++temprStart)
	{
		tempData[i++] = sourceData[temprStart];
	}
	for (int j=lStart; j<i; ++j)
	{
		sourceData[j] = tempData[j];
	}
	cout<< lStart<<" "<<lEnd<<" "<<rStart<<" "<<rEnd<<":"<<exchangeTime<<endl;
	return exchangeTime;
}


int mergesort(int sourceData[MAXNUM], int begin, int end)
{
	if (begin == end)
		return 0;
	else
	{
		int mid = (begin + end)>>1;
		return mergesort(sourceData, begin, mid) + mergesort(sourceData, mid+1, end) + merge(sourceData, begin, mid, mid+1, end);
	}
}</span>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值