【C语言】最长子序列问题求解

 二维数组,求取最长递增子序列,要求前一个点的横纵坐标都必须小于后面的点,且该点对应的值应不大于后续的值。

以每个点为起点的最长递增子序列

3

2

4

6

2

1

1

8

1

5

2

4

7

6

7

4

MAZE[SIZE][SIZE] – 对应的数组

 

 

 

1

 

 

 

1

 

 

?

1

1

1

1

1

 value[SIZE][SIZE] - 记录每一个点为起点的最长子序列的长度

最后一行 最后一列 都填写 1

 

 

 

1

 

 

 

1

 

 

?

1

1

1

1

1

 求value(i,j) 的值。

常规解法:遍历 i+1,j+1 到 size-1,size-1 里面所有的 value值,满足,MAZE[i][j] < MAZE[k][m] 时,求取最大的 value值。这样做需要四层 For 循环,会造成超时。

超时源代码:

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

#define MAX_SIZE 257

int Answer;
int size;
int MAZE[MAX_SIZE][MAX_SIZE];
int temp[MAX_SIZE][MAX_SIZE];

void count (int i, int j);

int main(void)
{
	int T, test_case;
	freopen("input.txt", "r", stdin);

	setbuf(stdout, NULL);

	scanf("%d", &T);
	for(test_case = 0; test_case < T; test_case++)
	{
		Answer = 0;

		int i = 0;
		int j = 0;
		size = 0;

		memset(MAZE,0,sizeof(MAZE));
		memset(temp,0,sizeof(temp));

		scanf("%d",&size);

		for (i = 0; i < size; i++)
		{
			for (j = 0; j < size; j++)
			{
				scanf("%d",&MAZE[i][j]);

				if (i == size-1 || j == size-1)
				{
					temp[i][j] = 1;
				}
			}
		}

		for (i = size-2; i >= 0; i--)
		{
			for (j = size-2; j >= 0; j--)
			{
				if (temp[i][j] != 0)
					continue;

				count(i,j);
				Answer = Answer > temp[i][j] ? Answer : temp[i][j];
			}
		}

		printf("Case #%d\n", test_case+1);
		printf("%d\n", Answer);
	}

	return 0;
}

void count (int i, int j)
{
	int k,m;

	for (k = i+1; k < size-temp[i][j]; k++)
	{
		for (m = j+1; m < size-temp[i][j]; m++)
		{
			if (MAZE[k][m] >= MAZE[i][j])
			{
				if (temp[k][m]+1 > temp[i][j] && temp[k][m]+1 > temp[i][j])
				{
					temp[i][j] = temp[k][m]+1;
				}
			}
		}
	}
}

于是,我们需要储存中间的值,以减少遍历的次数,达到优化算法的目的。

那么问题来了,也是很不好想的是,如何保存中间值??

最后的想法是,每一个value值只存储对应的最大的MAZE值,因为MAZE越大,组成最长子序列的长度的概率会越大。

于是想到,新建一个一维数组,vmax[SIZE]。vmax[i] 对应着 value = i 时 对应的最大MAZE值。

对于value(i,j),需要组成 i+1,j+1 到 size-1,size-1 对应的 vmax[SIZE] 数组。

3

2

4

6

2

1

1

8

1

5

2

4

7

6

7

4

 

 

 

 

1

 

 

 

1

 

 

?

1

1

1

1

1

Vmax[1] = 4;

 

 

 

1

 

 

 

1

 

?

2

1

1

1

1

1

 Vmax[1] = 7 > 4 ? 7 : 4 = 7

 

 

 

1

 

 

 

1

?

2

2

1

1

1

1

1

 Vmax[1] = 7 > 6 ? 7 : 6 = 7

到达第二行,清空 Vmax数组,可以看出关系,每一行对应的只是应该把之前没有访问过得点,进行比较更新 Vmax数组对应的值。每次只是让新的一列进入更新。

 

 

 

1

 

 

?

1

2

2

2

1

1

1

1

1

 

 

 

 

1

 

?

3

1

2

2

2

1

1

1

1

1

以下是程序:

		for (i = size-2; i >= 0; i--)
		{
			flag = 0;
			memset(vmax,0,sizeof(vmax));

			for (j = size-2; j >= 0; j--)
			{
				for (k = i+1; k < size; k++)
				{
					vmax[temp[k][j+1]] =
							vmax[temp[k][j+1]] > MAZE[k][j+1] ? vmax[temp[k][j+1]] : MAZE[k][j+1];
					flag = temp[k][j+1] > flag ? temp[k][j+1] : flag;
				}

				int find = 0;

				for (m = flag; m > 0; m--)
				{
					if (MAZE[i][j] <= vmax[m])
					{
						find = 1;
						temp[i][j] = m+1;
						break;
					}
				}

				if (!find)
				{
					temp[i][j] = 1;
				}
			}
		}
<span style="font-size:18px;">///</span>
<span style="font-size:18px;">	全部的源代码如下:</span>
<span style="font-size:18px;">//</span>
include <stdio.h>
#include <string.h>

#define MAX_SIZE 257

int Answer;
int size;
int MAZE[MAX_SIZE][MAX_SIZE];
// 以 i j 为起点的最长子序列
int temp[MAX_SIZE][MAX_SIZE];
int vmax[MAX_SIZE];

int main(void)
{
	int T, test_case;
	freopen("input.txt", "r", stdin);

	setbuf(stdout, NULL);

	scanf("%d", &T);
	for(test_case = 0; test_case < T; test_case++)
	{
		int i = 0;
		int j = 0;
		int k = 0;
		int m = 0;
		int flag = 0;

		size = 0;
		Answer = 0;

		memset(MAZE,0,sizeof(MAZE));
		memset(temp,0,sizeof(temp));
		memset(vmax,0,sizeof(vmax));

		scanf("%d",&size);

		for (i = 0; i < size; i++)
		{
			for (j = 0; j < size; j++)
			{
				scanf("%d",&MAZE[i][j]);

				if (i == size-1 || j == size-1)
				{
					temp[i][j] = 1;
				}
			}
		}

		for (i = size-2; i >= 0; i--)
		{
			flag = 0;
			memset(vmax,0,sizeof(vmax));

			for (j = size-2; j >= 0; j--)
			{
				for (k = i+1; k < size; k++)
				{
					vmax[temp[k][j+1]] =
							vmax[temp[k][j+1]] > MAZE[k][j+1] ? vmax[temp[k][j+1]] : MAZE[k][j+1];

					flag = temp[k][j+1] > flag ? temp[k][j+1] : flag;
				}

				int find = 0;

				for (m = flag; m > 0; m--)
				{
					if (MAZE[i][j] <= vmax[m])
					{
						find = 1;
						temp[i][j] = m+1;

						if (m == flag && temp[i][j] > Answer)
						{
							Answer = temp[i][j];
						}

						break;
					}
				}

				if (!find)
				{
					temp[i][j] = 1;
				}
			}
		}

		printf("Case #%d\n", test_case+1);
		printf("%d\n", Answer);
	}

	return 0;
}
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值