GDUT_寒假训练题解报告_专题I_A题 个人题解报告

GDUT_寒假训练题解报告_专题I_A题 个人题解报告

A题:尺取(模板题目

给出了一个N长度的正整数序列(10<N<1e5),每个正整数小于或等于10000,以及一个正整数S(S<1e8)。编写一个程序,以求序列的连续元素的子序列的最小长度,其和大于或等于S。
输入
第一行是测试用例的数量。对于每个测试用例,程序必须从第一行读取数字N和S,用间隔分隔。序列的数目在测试用例的第二行中给出,用间隔分隔。输入将以文件的结尾结束。
输出
对于每一种情况,程序必须在输出文件的单独行上打印结果。如果没有答案,则打印0。
样本输入
2
10 15
5 1 3 5 10 7 4 9 2 8
5 11
1 2 3 4 5
样本输出
2
3

分析:典型的尺取题目,如果暴力做就要设一个起点一个终点走上n(n-1)/2遍,但是用笔一划可以分析出来有些情况完全可以省略,比如序列(l,r),如果满足条件,那么(l,r+1)显然没必要了,因为徒增长度。
那么实际上对于(1,k)这个序列,到底怎么划分呢?

猜想:设原序列里面每个元素分别为(A(0),A(1)…A(n-1))状态f(x),意义为以第x元素结尾的满足条件序列的长度为L,也就是( A(x-L+1)…A(x) )这个序列
状态转移:f(x+1)如果只是尾巴A(x+1)衔上f(x),那么无疑会导致出现分析中的情况,但是因为意义是以x+1结尾的,所以呢,我们从A(x-L+1)开始查看,这个元素可不可以删去?如果可以,那就删去,那我f(x+1)长度就从L+1到了L,循环这个步骤,直到头部不可删去,此时f(x+1)达到了自身意义:以x+1号元素结尾的满足条件的最短序列的长度。

考虑:这个猜想似乎是正确的,每次尾部挪一个位置,然后从头部开始缩,直到缩到不能缩,每次有了一个新的长度,用来更新 int _MIN 的值,就可以了

毛毛虫算法,尺取算法十分有趣,同时加强了我对状态这两个字的理解,

上代码:

using namespace std;
int t,n,s;
int all[100010];
int main()
{
	while ( scanf ( "%d", &t ) != EOF )
	{
		while ( t-- )
		{
			scanf ( "%d %d", &n, &s );
			all[0] = 0;
			for ( int time = 1; time <= n; time++ )
			{
				int num;
				scanf ( "%d", &num );
				all[time] = all[time - 1] + num;
			}
			int _Min = n + 1;
			int left = 0, right = 1;
			if ( all[n] < s )
				printf ( "0\n" );
			else
			{
				while ( right <= n )
				{
					if ( right - left < _Min && all[right] - all[left] >= s )
						_Min = right - left;
					right++;
					while ( all[right] - all[left + 1] >= s )
					{
						left++;
					}
				}
				printf ( "%d\n", _Min );
			}
		}
	}

	return 0;
}

发布了8 篇原创文章 · 获赞 0 · 访问量 118

猜你喜欢

转载自blog.csdn.net/DevourPower/article/details/103950956