程序设计课 期末考试复习 专题四:线性结构

第四个专题是:线性数据结构

线性数据结构有很多:一维数组、指针、栈、队列、还有STL里面定义的一些数据结构等等。这些线性结构在解决一些问题方面也是很有用的。一维数组,大家都很清楚,有固定长度,有下标,每一个下标的位置对应一个值;指针,也可以看成一维数组;栈是一个先进后出的数据结构;队列是一个先进先出的数据结构,等等。

这一专题也没什么好写的,只能说做题,利用这些线性数据结构来完成一些题目。

1. Largest Rectangle in a Histogram (HDU 1506:https://vjudge.net/problem/HDU-1506 )

题意:求一组相邻单位宽度的长方形所能组成的最大长方形的面积。

如图:左边长方形集合中能组成最大的长方形面积就是右面阴影部分。

要求求得这个阴影部分的面积。

题解:可以看出,这些矩形的宽度是一样的,那么要求组合之后的最大面积,我们可以想象一个“吞并”的方法。任意一个位置的长方形都可以“吞并”它周围比它高的长方形,在比它低的长方形面前停下。然后利用这个长方形的高度乘以“吞并”的长方形总个数,就是这个长方形可以扩展的最大面积。这样这样我们就可以求出最大长方形面积了。

具体实现的数据结构可以是单调栈。

转化为另一个小问题:在一个一维数组中求左边和右边第一个小于它的元素的位置。算法伪代码就是:从左到右遍历一遍数组,栈顶存放的是遍历过程中最小的元素的下标。因为,越靠右而且越小的元素  总是  它左边比它还大的元素  是后面元素先往左找到的比当前元素要小的元素位置。

stack 栈 s,存入的是每个元素的下标。L[i] 数组用于存放每个小于它的左边元素。对于数组 a[] 。

for :i->n

    while(栈不空&&栈顶元素要不小于当前元素)

         弹出栈顶元素

   if (栈空)

     L[i]=0(记录左边比之小的元素为0)

   else  L[i] = 栈顶

   当前元素下标入栈。

用于解题的代码:

#include<iostream>
#include<stack>
using namespace std;

long long a[100005];
struct cfx
{
	int l;
	int r;
};

int main()
{
	int n;
	while(cin>>n)
	{
		if(n==0) break;
		long long ans=0;
		cfx dis[100005]; 
		for(int i=1;i<=n;i++)
		    cin>>a[i];
		stack<int> s;
		for(int i=1;i<=n;i++)
		{
			while(!s.empty()&&a[s.top()]>=a[i])
			    s.pop();
			if(s.empty())
			    dis[i].l=0;
			else dis[i].l=s.top();
			s.push(i);
		}
		stack<int> k;
		for(int i=n;i>=1;i--)
		{
			while(!k.empty()&&a[k.top()]>=a[i])
			    k.pop();
			if(k.empty())
			    dis[i].r=n+1;
			else dis[i].r=k.top();
			k.push(i);
		}
		for(int i=1;i<=n;i++)
		{
			if((dis[i].r-dis[i].l-1)*a[i] > ans)
			    ans=(dis[i].r-dis[i].l-1)*a[i];
		}
		cout<<ans<<endl;
	}
	return 0;
}

中间有两部分运用单调栈来解决的问题,一次是往左找,另一次是往右找。

最后再来个相减求出长度来就能求出面积来了。

2.Jessica‘s Reading Problem (poj3320)

Jessica 要读一本书,这本书有p页,第i页恰好有一个知识点ai(每个知识点都有不一样的编号)。全书中的知识点可能会被多次提到。考试之前,她要把所有知识点都复习了。

本题利用线性数据结构是 一维数组,利用的方法是尺取法。

尺取法我的理解:从这个序列中定义一段,这个“段”有开始节点和结束节点。初始化时,开始节点等于末尾节点,通过不断移动末尾节点,来判断这一段是否已经是符合要求的一段,如果是,那么就移动开始节点,直到末尾节点到达序列的末尾,从之前到达过的所有这些“段”的状态来判断一个最符合的结果。

当时AC的代码(可能有点多余):

#include<cstdio>
#include<iostream>
#include<map>
#include<set>
#pragma   warning(disable:4996)
using namespace std;
int min(int a, int b)
{
	return a < b ? a : b;
}
int main()
{
	int P;
	while (cin >> P)
	{
		set<int> se;
		int *a = new int[P + 1];

		for (int i = 0; i < P; i++)
		{
			scanf("%d", &a[i]);
			se.insert(a[i]);
		}
			
		int n = 0;
		int cnt = se.size();
		/*for (int i = 1; i < P; i++)
		{
			for (int j = 0; j < i; j++)
			{
				if (a[j] == a[i])
				{
					cnt--;
					break;
				}
			}
		}*/
		//cout << cnt << endl;
		int s = 0, t = 0, num = 0;
		map <int, int> count;
		int ans = P;
		for (;;)
		{
			while (t < P&&num < cnt)
			{
				if (count[a[t]] == 0)
				{
					num++;
				}
				count[a[t]]++;
				t++;
			}
			if (num < cnt) break;
			ans = min(ans, t - s);
			count[a[s]]--;
			if (count[a[s]] == 0)
			{
				num--;
			}
			s++;
		}
		delete[]a;
		cout << ans << endl;
	}
	
	return 0;
}

对于上面的代码,还是有几点要说明的。首先直接的尺取算法是按照学长的来的;利用set是因为set定义的数据结构里面有一个好用的功能就是可以输出这个序列中不同元素的个数,即知识点的个数;然后map是为了记录每个知识点出现的次数。

然后这个代码的问题就是:set在插入的时候用了很长的时间。。。

3.Sequence(poj3061)(尺取法之二)

长度为n的数列,有a0,a1,a2...an-1 ,和一个整数S。求这个序列中连续子序列总和不小于S的长度的最小值。如果解不存在那么就输出0。例如:

Sample Input

2
10 15
5 1 3 5 10 7 4 9 2 8
5 11
1 2 3 4 5

Sample Output

2
3

同样利用尺取法,首先初始化s=t=sum=0;while(sum<S)那么就t++,sum增加;如果无法满足sum>=S的话,就终止,并且更新最小值为min(ans,t-s);如果满足了sum>=S,那么要更改ans,同时减去as,再来进入下一次的判断或者增加t。

具体的代码:

#include<iostream>
using namespace std;
int a[100005];
int min(int a,int b)
{
	return a<b?a:b;
}
int main()
{
	int t,n,S;
	cin>>t;
	while(t--)
	{
		cin>>n>>S;
		for(int i=0;i<n;i++)
		    cin>>a[i];
		int s=0,t=0,sum=0;
		int ans=n+1;
		while(true)
		{
			while(t<n&&sum<S)
			{
				sum+=a[t++];
			}
			if(sum<S) break;
			ans=min(ans,t-s);
			sum=sum-a[s];
			s++;
		}
		if(ans>n) ans=0;
		cout<<ans<<endl;	
	}
	return 0;
}

线性结构也就写这么多吧。其实在STL里面有很多很多的好用的线性结构,什么优先队列,set,map,pair 等等,都有各自的长处。

结合上面三个问题吧,能用在线性结构上面的算法有很多,上面也就两种,个人比较菜(下定决心这个暑假狠劲刷题...)。不过话说回来,尺取法和单调栈单调队列都是在解题时很好用的方法,可以在很大程度上优化你的程序单纯用暴力方式求解时的时间复杂度。

发布了6 篇原创文章 · 获赞 0 · 访问量 184

猜你喜欢

转载自blog.csdn.net/morning_zs202/article/details/92636428