【C】动态规划--最大连续子序列和/最长不下降子序列/最长公共子序列/最长回文子串/DAG最长路

1007. Maximum Subsequence Sum (25)

时间限制
400 ms
内存限制
65536 kB
代码长度限制
16000 B
判题程序
Standard
作者
CHEN, Yue

Given a sequence of K integers { N1, N2, ..., NK }. A continuous subsequence is defined to be { Ni, Ni+1, ..., Nj } where 1 <= i <= j <= K. The Maximum Subsequence is the continuous subsequence which has the largest sum of its elements. For example, given sequence { -2, 11, -4, 13, -5, -2 }, its maximum subsequence is { 11, -4, 13 } with the largest sum being 20.

Now you are supposed to find the largest sum, together with the first and the last numbers of the maximum subsequence.

Input Specification:

Each input file contains one test case. Each case occupies two lines. The first line contains a positive integer K (<= 10000). The second line contains K numbers, separated by a space.

Output Specification:

For each test case, output in one line the largest sum, together with the first and the last numbers of the maximum subsequence. The numbers must be separated by one space, but there must be no extra space at the end of a line. In case that the maximum subsequence is not unique, output the one with the smallest indices i and j (as shown by the sample case). If all the K numbers are negative, then its maximum sum is defined to be 0, and you are supposed to output the first and the last numbers of the whole sequence.

Sample Input:
10
-10 1 2 3 4 -5 -23 3 7 -21
Sample Output:
10 1 4
#include<iostream>
#include<stdio.h>
using namespace std;
const int maxn=10010;
struct DP{
	int start,end;
	int data;
};
int main(){
	int n,i;
	cin>>n;
	int num=0;
	//while(n!=0){
		DP dp[maxn];
		int a[maxn];
		for(i=0;i<n;i++){
			cin>>a[i];
			if(a[i]<0) num++;
		}
		if(num==n){
			cout<<"0 "<<a[0]<<" "<<a[n-1]<<endl;
			return 0;
		}
		dp[0].data=a[0];//dp[i]=max(dp[i-1]+a[i],a[i])
		dp[0].end=dp[0].start=0;
		for(i=1;i<n;i++){
			if(dp[i-1].data+a[i]>a[i]){
				dp[i].data=dp[i-1].data+a[i];
				dp[i].start=dp[i-1].start;
				dp[i].end=i;
			}
			else if(dp[i-1].data+a[i]<a[i]){
				dp[i].data=a[i];
				dp[i].start=dp[i].end=i;
			}
			else{
				dp[i].data=a[i];
				dp[i].start=dp[i].end=i;
			}
		}
		int maxx=dp[0].data;
		int j;
		for(i=0;i<n;i++){
			if(dp[i].data>maxx){
				j=i;
				maxx=dp[i].data;
			}
		}
		printf("%d %d %d\n",maxx,a[dp[j].start],a[dp[j].end]);
		//cin>>n;
	//}
	return 0;
}

问题 A: 最大连续子序列

时间限制: 1 Sec   内存限制: 32 MB 提交: 69   解决: 39[ 提交][ 状态][ TK题库][命题人: ]

题目描述

给定K个整数的序列{ N1, N2, ..., NK },其任意连续子序列可表示为{ Ni, Ni+1, ..., Nj },其中 1 <= i <= j <= K。最大连续子序列是所有连续子序列中元素和最大的一个,例如给定序列{ -2, 11, -4, 13, -5, -2 },其最大连续子序列为{ 11, -4, 13 },最大和为20。现在增加一个要求,即还需要输出该子序列的第一个和最后一个元素。

输入

测试输入包含若干测试用例,每个测试用例占2行,第1行给出正整数K( K<= 10000 ),第2行给出K个整数,中间用空格分隔,每个数的绝对值不超过100。当K为0时,输入结束,该用例不被处理。

输出

对每个测试用例,在1行里输出最大和、最大连续子序列的第一个和最后一个元素,中间用空格分隔。如果最大连续子序列不唯一,则输出序号i和j最小的那个(如输入样例的第2、3组)。若所有K个元素都是负数,则定义其最大和为0,输出整个序列的首尾元素。

样例输入

5
-3 9 -2 5 -4
3
-2 -3 -1
0

样例输出

12 9 5
0 -2 -1

提示



这是一道稍微有点难度的动态规划题。



首先可以想到的做法是枚举每个区间的和,预处理sum[i]来表示区间[1, i]的和之后通过减法我们可以O(1)时间获得区间[i, j]的和,因此这个做法的时间复杂度为O(n^2)。



然后这题的数据范围较大,因此还需作进一步优化才可以AC。记第i个元素为a[i],定义dp[i]表示以下标i结尾的区间的最大和,那么dp[i]的计算有2种选择,一种是含有a[i-1],一种是不含有a[i-1],前者的最大值为dp[i-1]+a[i],后者的最大值为a[i]。而两者取舍的区别在于dp[i-1]是否大于0。

//问题A: 最大和连续子序列
#include<stdio.h>
#include<iostream>
using namespace std;
struct SUM{
	int start,end;
	int data;
};
int main(){
	int n,i;
	cin>>n;
	while(n!=0){
		int num=0;
		int a[10010];
		SUM dp[10010];
		for(i=0;i<n;i++){
			cin>>a[i];
			if(a[i]<0) num++;
		}
		if(num==n){
			cout<<"0 "<<a[0]<<" "<<a[n-1]<<endl;
		}
		else{
			dp[0].data=a[0];
			dp[0].start=dp[0].end=0;
			for(i=1;i<n;i++){
				if(a[i]+dp[i-1].data>=a[i]){
					dp[i].data=a[i]+dp[i-1].data;
					dp[i].start=dp[i-1].start;
					dp[i].end=i;
				}
				else{
					dp[i].data=a[i];
					dp[i].start=dp[i].end=i;
				}
			}
			int maxx=dp[0].data;
			int j=0;
			for(i=1;i<n;i++){
				//cout<<"i:"<<i<<"dp[i]:"<<dp[i].data<<endl;
				if(dp[i].data>maxx){
					maxx=dp[i].data;
					j=i;
				}
			}
			printf("%d %d %d\n",dp[j].data,a[dp[j].start],a[dp[j].end]);
		}
		cin>>n;
	}
	return 0;
}

问题 A: 最长上升子序列

时间限制: 2 Sec  内存限制: 64 MB
提交: 73  解决: 51
[ 提交][ 状态][ TK题库][命题人:]

题目描述

一个数列ai如果满足条件a1 < a2 < ... < aN,那么它是一个有序的上升数列。我们取数列(a1, a2, ..., aN)的任一子序列(ai1, ai2, ..., aiK)使得1 <= i1 < i2 < ... < iK <= N。例如,数列(1, 7, 3, 5, 9, 4, 8)的有序上升子序列,像(1, 7), (3, 4, 8)和许多其他的子序列。在所有的子序列中,最长的上升子序列的长度是4,如(1, 3, 5, 8)。

    现在你要写一个程序,从给出的数列中找到它的最长上升子序列。

输入

输入包含两行,第一行只有一个整数N(1 <= N <= 1000),表示数列的长度。

第二行有N个自然数ai,0 <= ai <= 10000,两个数之间用空格隔开。

输出

输出只有一行,包含一个整数,表示最长上升子序列的长度。

样例输入

71 7 3 5 9 4 8

样例输出

4
//问题A: 最长上升子序列
#include<stdio.h>
#include<iostream>
using namespace std;
int main(){
	int n;
	int buf[1010];
	int dp[1010]={0};
	cin>>n;
	int i,j;
	for(i=0;i<n;i++){
		cin>>buf[i];
	}
	int ans=-1;
	for(i=0;i<n;i++){
		dp[i]=1;
		for(j=0;j<i;j++){
			if(buf[j]<=buf[i]&&dp[j]+1>dp[i]){
				dp[i]=dp[j]+1;
			}
		}
		ans=ans>dp[i]?ans:dp[i];
	}
	printf("%d",ans);
	return 0;
}

问题 A: 最长公共子序列

时间限制: 1 Sec   内存限制: 32 MB
提交: 63   解决: 49
[ 提交][ 状态][ TK题库][命题人: ]

题目描述

给你一个序列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
//问题A: 最长公共子序列
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
int max(int a,int b){
	return a>b?a:b;
}
int main(){
	char a[110],b[110];
	while(scanf("%s %s",a,b)!=EOF){
		int i,j;
		int dp[110][110];
		int len1=strlen(a);
		int len2=strlen(b);
		char aa[110],bb[110];
		for(i=0;i<len1;i++){
			aa[i+1]=a[i];
		}
		for(i=0;i<len2;i++){
			bb[i+1]=b[i];
		}
		for(i=0;i<=len1;i++){
			dp[i][0]=0;
		}
		for(i=0;i<=len2;i++){
			dp[0][i]=0;
		}
		for(i=1;i<=len1;i++){
			for(j=1;j<=len2;j++){
				if(aa[i]==bb[j]){
					dp[i][j]=dp[i-1][j-1]+1;
				}
				else{
					dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
				}
			}
		}
		printf("%d\n",dp[len1][len2]);
	}
	return 0;
}

问题 A: 【字符串】最长回文子串

时间限制: 1 Sec   内存限制: 128 MB
提交: 43   解决: 24
[ 提交][ 状态][ TK题库][命题人: ]

题目描述

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

输入

一行字符串,字符串长度不超过5000。

输出

字符串中的最长回文子串。

样例输入

Confuciuss say:Madam,I'm Adam.

样例输出

Madam,I'm Adam
//问题A: 【字符串】最长回文子串
//去掉空格、标点、大小写,判断回文
//输出原文
#include<stdio.h>
#include<string.h>
struct STR{
	char a;
	int no;
}str[5010];
int dp[5010][5010];
int deal(char &a){
	if(a>='0'&&a<='9') return 1;
	if(a>='a'&&a<='z'){
		a=a-'a'+'A';
		return 1;
	}
	if(a>='A'&&a<='Z') return 1;
	return 0;
}

int main(){
	memset(dp,0,sizeof(dp));
	char buf[5010];
	gets(buf);
	int len=strlen(buf);
	int i,j=0,k;
	for(i=0;i<len;i++){//只保留字母和数字,同时将小写转换为大写
		char a=buf[i];
		if(deal(a)==1){
			str[j].a=a;
			str[j].no=i;//原来的位置
			j++;
		}
	}
	int len2=j;//需要判断的字符串的长度
	int ans=1;//当前回文串长度
	int begin=0;
	for(i=0;i<len2;i++){
		dp[i][i]=1;
		if(i<len2-1){
			if(str[i].a==str[i+1].a){
				dp[i][i+1]=1;
				if(ans!=2) begin=i;//第一次更新
				ans=2;
			}
		}
	}
	for(int L=3;L<=len2;L++){
		for(i=0;i+L-1<len2;i++){
			j=i+L-1;
			if(dp[i+1][j-1]==1&&str[i].a==str[j].a){
				dp[i][j]=1;
				if(ans!=L) begin=i;//L长度的第一次更新
				ans=L;
			}
		}
	}
	//printf("%d %d\n",begin,ans);
	//printf("%d %d\n",str[begin].no,str[begin+ans-1].no);
	for(i=str[begin].no;i<=str[begin+ans-1].no;i++){
		printf("%c",buf[i]);
	}
	return 0;
}

1040. Longest Symmetric String (25)

时间限制
400 ms
内存限制
65536 kB
代码长度限制
16000 B
判题程序
Standard
作者
CHEN, Yue

Given a string, you are supposed to output the length of the longest symmetric sub-string. For example, given "Is PAT&TAP symmetric?", the longest symmetric sub-string is "s PAT&TAP s", hence you must output 11.

Input Specification:

Each input file contains one test case which gives a non-empty string of length no more than 1000.

Output Specification:

For each test case, simply print the maximum length in a line.

Sample Input:
Is PAT&TAP symmetric?
Sample Output:
11
#include<stdio.h>
#include<string>
#include<iostream>
#include<sstream>
#include<string.h>
using namespace std;
int dp[1001][1001];
int main(){
	string str;
	getline(cin,str);
	memset(dp,0,sizeof(dp));
	int i;
	int ans=1;
	for(i=0;i<str.size();i++){
		dp[i][i]=1;
		if(i<str.size()-1){	
			if(str[i]==str[i+1]){
				dp[i][i+1]=1;
				ans=2;
			}
		}
	}
	int L;
	for(L=3;L<=str.size();L++){
		for(i=0;i+L-1<str.size();i++){
			int j=i+L-1;
			if(str[i]==str[j]&&dp[i+1][j-1]==1){
				dp[i][j]=1;
				ans=L;
			}
		}
	}
	printf("%d",ans);
	return 0;
}

1045. Favorite Color Stripe (30)

时间限制
200 ms
内存限制
65536 kB
代码长度限制
16000 B
判题程序
Standard
作者
CHEN, Yue

Eva is trying to make her own color stripe out of a given one. She would like to keep only her favorite colors in her favorite order by cutting off those unwanted pieces and sewing the remaining parts together to form her favorite color stripe.

It is said that a normal human eye can distinguish about less than 200 different colors, so Eva's favorite colors are limited. However the original stripe could be very long, and Eva would like to have the remaining favorite stripe with the maximum length. So she needs your help to find her the best result.

Note that the solution might not be unique, but you only have to tell her the maximum length. For example, given a stripe of colors {2 2 4 1 5 5 6 3 1 1 5 6}. If Eva's favorite colors are given in her favorite order as {2 3 1 5 6}, then she has 4 possible best solutions {2 2 1 1 1 5 6}, {2 2 1 5 5 5 6}, {2 2 1 5 5 6 6}, and {2 2 3 1 1 5 6}.

Input Specification:

Each input file contains one test case. For each case, the first line contains a positive integer N (<=200) which is the total number of colors involved (and hence the colors are numbered from 1 to N). Then the next line starts with a positive integer M (<=200) followed by M Eva's favorite color numbers given in her favorite order. Finally the third line starts with a positive integer L (<=10000) which is the length of the given stripe, followed by L colors on the stripe. All the numbers in a line are separated by a space.

Output Specification:

For each test case, simply print in a line the maximum length of Eva's favorite stripe.

Sample Input:
6
5 2 3 1 5 6
12 2 2 4 1 5 5 6 3 1 1 5 6
Sample Output:
7
//最长不下降子序列解法
#include<stdio.h>
#include<iostream>
using namespace std;
const int maxn=100010;
int hhash[201];//为个可能喜欢的颜色建hash
int buf[maxn];
int dp[maxn];
int main(){
	int n,m,i;
	cin>>n;//color编号-n
	for(i=0;i<201;i++) hhash[i]=-1;
	cin>>m;
	for(i=0;i<m;i++){
		int a;
		cin>>a;
		hhash[a]=i;
	}
	cin>>m;
	int j=0,k;
	for(i=0;i<m;i++){
		int a;
		cin>>a;
		if(hhash[a]!=-1) buf[j++]=hhash[a];//只筛选出喜欢的,并且转化为hash值
	}
	int ans=-1;
	for(i=0;i<j;i++){
		dp[i]=1;
		for(k=0;k<i;k++){
			if(buf[k]<=buf[i]&&dp[k]+1>dp[i]){
				dp[i]=dp[k]+1;
			}
		}
		ans=ans>dp[i]?ans:dp[i];
	}
	printf("%d",ans);
	return 0;
}
//最长不下降子序列解法
#include<stdio.h>
#include<iostream>
using namespace std;
const int maxn=100010;
int buf[maxn];
int dp[201][maxn];
int a[201]={0};
int b[maxn];
int main(){
	int n,m1,m2,j,i;
	cin>>n;//color编号-n
	cin>>m1;
	for(i=1;i<=m1;i++){
		cin>>a[i];
	}
	cin>>m2;
	for(i=1;i<=m2;i++){
		cin>>b[i];
	}
	for(i=0;i<=m1;i++) dp[i][0]=0;
	for(i=0;i<=m2;i++) dp[0][i]=0;
	for(i=1;i<=m1;i++){
		for(j=1;j<=m2;j++){
			if(a[i]==b[j]){
				dp[i][j]=max(dp[i-1][j],dp[i][j-1])+1;
			}
			else{
				dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
			}
		}
	}
	printf("%d",dp[m1][m2]);
	return 0;
}

问题 A: 矩形嵌套

时间限制: 1 Sec   内存限制: 64 MB
提交: 23   解决: 19
[ 提交][ 状态][ TK题库][命题人: ]

题目描述

有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
//问题A: 矩形嵌套
//若最长路径长度相同,选择字典序小的那个
//依次根据输入长宽判断能否嵌套
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
int dp[1010];
int G[1010][1010];
int choice[1010];
struct node{
	int a,b;
};
bool qiantao(node t1,node t2){
	if(t1.a<t2.a&&t1.b<t2.b) return true;
	if(t1.a<t2.b&&t1.b<t2.a) return true;
	return false;
}
int DP(int i,int m){//m是顶点数
	if(dp[i]>0) return dp[i];
	for(int j=0;j<m;j++){
		if(G[i][j]!=-1){
			int temp=DP(j,m)+G[i][j];
			if(temp>dp[i]){
				dp[i]=temp;
				choice[i]=j;
			}
		}
	}
	return dp[i];
}
int print(int i){//先要得到最大的dp[i]
	//printf("%d",i);
	int num=1;
	while(choice[i]!=-1){
		i=choice[i];num++;
		//printf("->%d",i);
	}
	return num;
}
	
int main(){
	int n,i,j,k;
	cin>>n;
	for(i=0;i<n;i++){
		node buf[1010];
		memset(dp,0,sizeof(dp));
		memset(G,-1,sizeof(G));
		memset(choice,-1,sizeof(choice));
		int m;
		cin>>m;
		for(j=0;j<m;j++){
			int a,b;
			cin>>a>>b;
			buf[j].a=a;
			buf[j].b=b;
		}
		for(j=0;j<m;j++){
			for(k=j+1;k<m;k++){
				if(qiantao(buf[j],buf[k])) G[j][k]=1;//j->k
				else if(qiantao(buf[k],buf[j])) G[k][j]=1;//k->j
			}
		}
		int max=-1;
		int u=-1;
		for(j=m-1;j>=0;j--){//字典序
			dp[j]=DP(j,m);
			if(dp[j]>max){
				max=dp[j];
				u=j;
			}
		}
		int num=print(u);
		printf("%d\n",num);
	}
	return 0;
}







  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值