《算法笔记》11.4 11.5 11.6小节——动态规划专题->最长公共子序列(LCS)->最长回文子串->DAG最长路

11.4最长公共子序列(LCS)

问题 A: 最长公共子序列
问题描述:给你一个序列X和另一个序列Z,当Z中的所有元素都在X中存在,并且在X中的下标顺序是严格递增的,那么就把Z叫做X的子序列。
例如:Z=<a,b,f,c>是序列X=<a,b,c,f,b,c>的一个子序列,Z中的元素在X中的下标序列为<1,2,4,6>。
现给你两个序列X和Y,请问它们的最长公共子序列的长度是多少?

  • 输入
输入包含多组测试数据。每组输入占一行,为两个字符串,由若干个空格分隔。每个字符串的长度不超过100
  • 输出
对于每组输入,输出两个字符串的最长公共子序列的长度。
  • 样例输入
abcfbc abfcab
programming contest 
abcd mnp
  • 样例输出
4
2
0

codeup会报编译错误,注意要加上cstring的头文件,注意下标是从1开始的,所以要

scanf("%s %s",A+1,B+1)

我现在才知道可以查看codeup的编译信息,orz,之前的编译错误都没管,感觉错过了一个亿。
在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 110;
int dp[maxn][maxn];
char A[maxn],B[maxn];
int main() {
	while (scanf("%s %s",A+1,B+1)!=EOF)
	{
		int lenA = strlen(A + 1);
		int lenB = strlen(B + 1);
		for (int i = 0; i <= lenA; i++)
		{
			dp[i][0] = 0;
		}
		for (int j = 0; j <= lenB; j++)
		{
			dp[0][j] = 0;
		}
		for (int i = 1; i <= lenA; i++)
		{
			for (int j = 1; j <= lenB; j++)
			{
				if (A[i] == B[j])
					dp[i][j] = dp[i - 1][j - 1] + 1;
				else
				{
					dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
				}
			}

		}
		printf("%d\n",dp[lenA][lenB]);
	}
	return 0;
}

11.5最长回文子串

问题 A: 【字符串】最长回文子串
问题描述: 输入一个字符串,求出其中最长的回文子串。子串的含义是:在原串中连续出现的字符串片段。回文的含义是:正着看和倒着看相同。如abba和yyxyy。在判断回文时,应该忽略所有标点符号和空格,且忽略大小写,但输出应保持原样(在回文串的首部和尾部不要输出多余字符)。输入字符串长度不超过5000,且占据单独的一行。应该输出最长的回文串,如果有多个,输出起始位置最靠左的。

  • 输入
一行字符串,字符串长度不超过5000
  • 输出
字符串中的最长回文子串。
  • 样例输入
Confuciuss say:Madam,I'm Adam.
  • 样例输出
Madam,I'm Adam

codeup又报编译错误了,怀疑它的库里面就没有gets函数呜呜

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#include<cstdio>
#include<stdio.h>
using namespace std;
const int maxn = 5010;
int dp[maxn][maxn];
char S[maxn];
char S1[maxn];//统一化之后的字符串
int p[maxn];
int main() {
	while (1)
	{
		gets_s(S);
		int num = 0;
		for (int i = 0; i < strlen(S); i++)
		{
			
			if (!isalpha(S[i]) && !isdigit(S[i]))//如果不是数字也不是字母
			{
				;
			}
			else if (isalpha(S[i]))//如果是字母
			{
				S1[num] = toupper(S[i]);				
				p[num++] = i;
			}
			else
			{
				S1[num] =S[i];				
				p[num++] = i;
			}
				
		}

		int len = strlen(S1);
		int ans = 1;
		int max = 0;
		int st=0, ed=0;
		memset(dp, 0, sizeof(dp));
		for (int i = 0; i < len; i++)
		{
			dp[i][i] = 1;
			if (ans > max)
			{
				max = ans;
				st = ed = p[i];
			}
			if(i < len - 1)
			{
				if (S1[i] == S1[i + 1])
				{
					dp[i][i + 1] = 1;
					ans = 2;
					if (ans > max)
					{
						max = ans;
						st = p[i];
						ed = p[i + 1];						
					}
				}
					
			}
		}
		for (int L = 3; L <= len; L++)
		{
			for (int i = 0; i + L - 1 < len; i++)
			{
				int j = i + L - 1;
				if (S1[i] == S1[j] && dp[i + 1][j - 1] == 1)
				{
					dp[i][j] = 1;
					ans = L;
					if (ans > max)
					{
						max = ans;
						st = p[i];
						ed = p[j];						
					}
				}
			}
		}
		
		//至此,找到了最左最长回文在原字符串的起始位置和终止位置st,ed;
		for (int i = st; i <= ed; i++)
			printf("%c",S[i]);
		printf("\n");
		
	}
	return 0;

}

11.6DAG最长路

DAG就是有向无环图
在前面关键路径那里用了四个数组ve,vl,e,l来更新,求解最长路径,下面用动态规划来求解,最长路和最短路原理一致
解决两个问题

11.6.1求整个DAG中的最长路径(不固定起点和终点)

可以用逆拓扑序列的方法来求解dp数组,
如果不用逆拓扑序列的方法,可以使用递归的方式;
递归需要用邻接矩阵来存储

11.6.2固定终点,求DAG中的最长路径

问题 A: 矩形嵌套
问题描述:有n个矩形,每个矩形可以用a,b来描述,表示长和宽。矩形X(a,b)可以嵌套在矩形Y(c,d)中当且仅当a<c,b<d或者b<c,a<d(相当于旋转X90度)。例如(1,5)可以嵌套在(6,2)内,但不能嵌套在(3,4)中。你的任务是选出尽可能多的矩形排成一行,使得除最后一个外,每一个矩形都可以嵌套在下一个矩形内。

  • 输入
第一行是一个正正数N(0<N<10),表示测试数据组数,
每组测试数据的第一行是一个正正数n,表示该组测试数据中含有矩形的个数(n<=1000)
随后的n行,每行有两个数a,b(0<a,b<100),表示矩形的长和宽
  • 输出
每组测试数据都输出一个数,表示最多符合条件的矩形数目,每组输出占一行
  • 样例输入
1
10
1 2
2 4
5 8
6 10
7 9
3 1
5 8
12 10
9 7
2 2
  • 样例输出
5

这题要注意build函数是怎么构造邻接矩阵的

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdio.h>
#include<vector>
using namespace std;
const int maxn = 1010;
int N, n;
int G[maxn][maxn];
int dp[maxn];
int choice[maxn];
struct node {
	int x, y;
	node(int x = 0, int y = 0) :x(x), y(y) {}
};
vector<node> vec;
void build()
{
	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++)
		{
			if (i != j)
			{
				int a, b, c, d;
				a = vec[i].x, b = vec[i].y;
				c = vec[j].x, d = vec[j].y;
				if (a < c && b < d || b < c && a < d) G[i][j] = 1;
			}
		}

}
int DP(int i)
{
	if (dp[i] > 0) return dp[i];
	dp[i] = 1;
	for (int j = 0; j < n; j++)
	{

		if (G[i][j])
		{
			int temp = DP(j) + G[i][j];
			if (temp > dp[i])
			{
				dp[i] = temp;
				choice[i] = j;
			}
		}
		
	}
	return dp[i];
}
void printPath(int i)
{
	printf("%d",i);
	while (choice[i] != -1)
	{
		i = choice[i];
		printf("->%d",i);
	}
	printf("\n");
}
int main() {
	scanf("%d", &N);
	for (int i = 1; i <= N; i++)
	{
		scanf("%d", &n);
		for (int j = 0; j < n; j++)
		{
			int x, y;
			scanf("%d%d", &x, &y);
			vec.push_back(node(x, y));
		}
		fill(G[0], G[0] + maxn * maxn, 0);
		fill(dp, dp + maxn, 0);
		fill(choice, choice + maxn, -1);
		build();
		for (int j = 0; j < n; j++)
		{
			dp[j]=DP(j);
		}
		int k = 0;
		for (int j = 1; j < n; j++)
		{
			if (dp[j] > dp[k])
				k = j;
		}		
		printf("%d\n", dp[k]);
		
		//printPath(k);
		vec.clear();
	}
		return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值