11.1
问题 A: Fibonacci
[命题人 : 外部导入]
时间限制 : 1.000 sec 内存限制 : 32 MB
题目描述
The Fibonacci Numbers{0,1,1,2,3,5,8,13,21,34,55…} are defined by the recurrence:
F0=0 F1=1 Fn=Fn-1+Fn-2,n>=2
Write a program to calculate the Fibonacci Numbers.
输入
Each case contains a number n and you are expected to calculate Fn.(0<=n<=30) 。
输出
For each case, print a number Fn on a separate line,which means the nth Fibonacci Number.
样例输入 Copy
1
样例输出 Copy
1
#include<stdio.h>
#define maxn 50
int dp[maxn];
int func(int n);
int main()
{
int n;
while(scanf("%d",&n)!=EOF){
for(int i=0;i<=n;i++)
dp[i]=-1;
int fib=func(n);
printf("%d\n",fib);
}
return 0;
}
int func(int n)
{
if(n==0) return 0;
if(n==1) return 1;
if(dp[n]!=-1) return dp[n];
else{
dp[n]=func(n-1)+func(n-2);
return dp[n];
}
}
用dp数组来记录已经计算过的数字,如果没有计算过,那么dp[n]=func(n-1)+func(n-2);
另外输入的时候必须用while…!=EOF,不然答案通不过
11.2最大子列和
#include<stdio.h>
#include<algorithm>
#define maxn 10010
using namespace std;
int a[maxn],dp[maxn];
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
dp[0]=a[0];
for(int i=0;i<n;i++){
dp[i]=max(a[i],dp[i-1]+a[i]);//状态转移方程
}
int k=0;
for(int i=0;i<n;i++){
if(dp[i]>dp[k]) k=i;
}
printf("%d\n",dp[k]);
return 0;
}
问题 A: 最大连续子序列
[命题人 : 外部导入]
时间限制 : 1.000 sec 内存限制 : 32 MB
题目描述
给定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,输出整个序列的首尾元素。
样例输入 Copy
5
-3 9 -2 5 -4
3
-2 -3 -1
0
样例输出 Copy
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。
#include<stdio.h>
#include<algorithm>
#define maxn 10010
using namespace std;
int a[maxn],dp[maxn],cnt[maxn]={0};
//cnt记录以a[i]为结尾的最长子列和中元素个数
int main()
{
int k;
while(scanf("%d",&k)!=EOF){
if(!k) break;
for(int i=0;i<k;i++){
scanf("%d",&a[i]);
}
int i;
for(i=0;i<k;i++){
if(a[i]>=0) break;
}
if(i==k){
printf("0 %d %d\n",a[0],a[k-1]);
}
else{
dp[0]=a[0];
cnt[0]=1;
for(int i=1;i<k;i++){
dp[i]=max(a[i],dp[i-1]+a[i]);
if(dp[i]==a[i]) cnt[i]=1;
else cnt[i]=cnt[i-1]+1;
}
int tmp=0;
for(int i=0;i<k;i++){
if(dp[i]>dp[tmp]) tmp=i;
}
printf("%d %d %d\n",dp[tmp],a[tmp-cnt[tmp]+1],a[tmp]);
}
}
return 0;
}
11.3最长不下降子序列(LIS)
#include<stdio.h>
#include<algorithm>
#define N 100
using namespace std;
int dp[N],a[N];
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
int ans=-1;//记录最大的dp[i]
for(int i=0;i<n;i++){
dp[i]=1;//边界条件
for(int j=0;j<i;j++){
if(a[j]<=a[i] && (dp[j]+1>dp[i]))
dp[i]=dp[j]+1;
}
ans=max(ans,dp[i]);
}
printf("%d",ans);
return 0;
}
问题 A: 最长上升子序列
[命题人 : 外部导入]
时间限制 : 2.000 sec 内存限制 : 64 MB
题目描述
一个数列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,两个数之间用空格隔开。
输出
输出只有一行,包含一个整数,表示最长上升子序列的长度。
样例输入 Copy
7
1 7 3 5 9 4 8
样例输出 Copy
4
#include <stdio.h>
#include<algorithm>
#define N 1010
using namespace std;
int dp[N],a[N];
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
int ans=-1;
for(int i=0;i<n;i++){
dp[i]=1;
for(int j=0;j<i;j++){
if(a[j]<a[i] && (dp[j]+1>dp[i]))
dp[i]=dp[j]+1;
}
ans=max(ans,dp[i]);
}
printf("%d\n",ans);
return 0;
}
11.4最长公共子序列(LCS)
状态转移方程:
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]);
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 100
using namespace std;
char a[N],b[N];
int dp[N][N];
int main()
{
gets(a+1);
gets(b+1);
int len1=strlen(a+1);
int len2=strlen(b+1);
for(int i=0;i<=len1;i++) dp[i][0]=0;
for(int i=0;i<=len2;i++) dp[0][i]=0;
for(int i=1;i<=len1;i++){
for(int j=1;j<=len2;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[len1][len2]);
return 0;
}
问题 A: 最长公共子序列
[命题人 : 外部导入]
时间限制 : 1.000 sec 内存限制 : 32 MB
题目描述
给你一个序列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。
输出
对于每组输入,输出两个字符串的最长公共子序列的长度。
样例输入 Copy
abcfbc abfcab
programming contest
abcd mnp
样例输出 Copy
4
2
0
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define N 110
char a[N],b[N];
int dp[N][N]={0};
int main()
{
while(scanf("%s %s",a+1,b+1)!=EOF){
int len1=strlen(a+1);
int len2=strlen(b+1);
for(int i=1;i<=len1;i++){
for(int j=1;j<=len2;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[len1][len2]);
}
return 0;
}
11.5最长回文子串
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define maxn 1010
using namespace std;
char s[maxn];
int dp[maxn][maxn];
int main()
{
gets(s);
int len=strlen(s);
int ans=1;
memset(dp,0,sizeof(dp));
//边界
for(int i=0;i<len;i++){
dp[i][i]=1;
if(i<len-1){
if(s[i]==s[i+1]){
dp[i][i+1]=1;
ans=2;//初始化注意当前最长回文子串长度
}
}
}
//状态转移方程
for(int l=3;l<=len;l++){
for(int i=0;i+l<len;i++){//枚举子串起始端点
int j=i+l-1;//子串右端点
if(s[i]==s[j] && dp[i+1][j-1]==1){
dp[i][j]=1;
ans=l;//更新最长回文子串长度
}
}
}
printf("%d\n",ans);
return 0;
}