目录
A 最长递增子序列
题目描述
给出一个序列a1,a2,a3,a4,a5,a6,a7...an,求它的一个子序列(设为s1,s2,...sn),使得这个子序列满足这样的性质:s1<s2<s3<...<sn并且这个子序列的长度最长。输出这个最长子序列的长度,要求时间复杂度为O(n2)。
输入
每组输入包括两行,第一行为序列长度n,第二行为序列。
输出
输出最长递增子序列的长度。
样例输入 Copy
7
1 7 3 5 9 4 8
样例输出 Copy
4
分析:这题需要用一个一维数组来记录,每次的最长递增序列长度,并找到最大值即可。
具体还是看代码:
代码实现:c语言
#include <stdio.h>
#include <stdlib.h>
int main (){
int a[2000];
int b[2000];
int n;
while(~scanf("%d",&n)){
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
int z=solve(a,b,n);
printf("%d",z);
printf("\n");
}
return 0;
}
int solve(int a[],int b[],int n){
b[0]=1;
int maxlen;
int max=b[0];
for(int i=1;i<n;i++)
{
maxlen=0;
for(int j=i-1;j>=0;j--)
{
if(a[j]<a[i]&&maxlen<b[j])
{
maxlen=b[j];
}
}
b[i]=maxlen+1;
if(b[i]>max)
max=b[i];
}
return max;
}
B 构造最长递增子序列
题目描述
在“最长递增子序列”的基础上对代码进行改进,输出一条最长递增子序列。
输入
每组输入包括两行,第一行为序列长度n,第二行为序列。
输出
输出最长递增子序列中的任意一条即可。
样例输入 Copy
7
1 7 3 4 9 2 3
样例输出 Copy
1 3 4 9
分析:在第一题的基础上,做一点点记录即可,比如:用一个一维数组来记录每次上一次的最大值的下标。
直接看代码分析:
代码实现:c语言
#include <stdio.h>
#include <stdlib.h>
int p[2000];
int main (){
int a[2000];
int b[2000];
int n;
while(~scanf("%d",&n)){
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
int z=solve(a,b,n);
printf("\n");
}
return 0;
}
int solve(int a[],int b[],int n){
b[0]=1;
int maxlen;
int max=b[0];
int x=0;
for(int i=1;i<n;i++)
{
maxlen=0;
for(int j=i-1;j>=0;j--)
{
if(a[j]<a[i]&&maxlen<b[j])
{
maxlen=b[j];
p[i]=j;
}
}
b[i]=maxlen+1;
if(b[i]>max){
max=b[i];
x=i;
}
}
int i=x;
int m=max;
int c[2000];
while(m>0){
c[m]=a[i];
i=p[i];
m--;
}
for(int i=1;i<=max;i++)
printf("%d ",c[i]);
return max;
}
C 0-1背包问题
题目描述
给定n种物品和一个背包,物品i的重量是Wi,其价值为Vi,背包的容量为C。如何选择装入背包的物品,可以使得装入背包中物品的总价值最大?
输入
每组输入包括三行,
第一行包括物品个数n,以及背包容量C。
第二、三行包括两个一维数组,分别为每一种物品的价值和重量。
输出
输出包括两行,第一行为背包的最大总价值,第二行为所选取的物品。
例如:最大总价值=15,物品选取策略为11001。数据保证答案唯一。
样例输入 Copy
5 10
6 3 5 4 6
2 2 6 5 4
样例输出 Copy
15
11001
分析:0-1背包问题是经典的填表问题,动态规划。
先填最下层,再依次往上面填。
设:tb[i][j] ,其中i表示第 i 个物品 ,j 表示 还剩下多少容量。而tb[i][j]本身代表 已经装了的物品的总价值。
所以可以 在 填第 最后一个物品时 ,也就是 i=n-1。
所以 if j < w[n-1] tb[n-1][j]=0 , 也就是 装不进。
if j>= w[n-1] tb[n-1][j] = v[n-1]
然后,如果 当 i<=n-2 时
if j>= w[i] tb[i][j] = max ( tb[i+1][j] , tb[i+1][j-w[i]] +v[i] ); (取两者当中得最大值)
if j< w[i] tb[i][j] = tb[i+1][j]; (直接取下一个背包);
所以代码分析得:
代码实现:c语言
#include <stdio.h>
#include <stdlib.h>
int tb[200][200];
int main (){
int w[200];
int v[200];
int n;
int c;
while(~scanf("%d %d",&n,&c)){
for(int i=0;i<n;i++)
scanf("%d",&v[i]);
for(int i=0;i<n;i++)
scanf("%d",&w[i]);
int z=solve(w,v,n,c);
printf("%d",z);
printf("\n");
traceback(w,c,n);
printf("\n");
}
return 0;
}
int solve(int w[],int v[],int n,int c){
int jmax = min(c,w[n-1]-1);
for(int j=0;j<=jmax;j++)
tb[n-1][j]=0;
for(int j=jmax+1;j<=c;j++)
tb[n-1][j]=v[n-1];
for(int i=n-2;i>=0;i--)
{
int jmax = min(c,w[i]-1);
for(int j=0;j<=jmax;j++)
tb[i][j]=tb[i+1][j];
for(int j=jmax+1;j<=c;j++)
tb[i][j]=max(tb[i+1][j],tb[i+1][j-w[i]]+v[i]);
}
return tb[0][c];
}
int max(int x,int y){
return x>y?x:y;
}
int min(int x,int y){
return x<y?x:y;
}
void traceback(int w[],int c,int n){
for(int i=0;i<n;i++){
if(tb[i][c]==tb[i+1][c]){
printf("0");
}
else{
c-=w[i];
if(c>=0)
printf("1");
else{
printf("0");
}
}
}
}
D x星人的基因
题目描述
X星人的基因由A、B、C、D、E五种不同的结构组合而成。
如果两个性别不同的X星人的基因序列相似度大于50%,按照X星的法律他们是禁止结婚的,等于50%据说还是可以的。
那么基因的相似度怎么计算呢?分别从两个人身上取长度均为N的基因片段,如果它们的最长公共子序列为M,则相似度=M/N。是不是很简单呢?
现在给你两段X星人的基因序列片段,请你判断他们是不是可以结婚?
输入
每一组测试数据包含3行,
第1行数字N表示待比较基因序列片段的长度,N<=10^3。
第2行和第3行为两个长度为N的基因序列片段。
输入0表示结束。
输出
两个X星人是否可以结婚,如果可以输出”Yes“,如果不可以输出”No“。
样例输入 Copy
8
A B C D E A B C
A C C D C B A E
6
A B C D E E
A E D C B B
0
样例输出 Copy
Yes
Yes
分析:此题其实题目很简单,就是 把最长公共子序列的长度求出来即可。
我们还是动态规划来写。
这题要注意的是,我们如何去读入这两行字符串。
具体请看代码分析:
代码实现:c语言
#include <stdio.h>
#include <stdlib.h>
int tb[2000][2000];
int main (){
int n;
char a[2000];
char b[2000];
while(~scanf("%d",&n)){
if(n==0) break;
getchar();
for(int i=0;i<n;i++)
{
scanf("%c",&a[i]);
getchar();
}
for(int i=0;i<n;i++)
{
scanf("%c",&b[i]);
getchar();
}
int z=solve(a,b,n);
double p=z*1.0/n;
if(p-0.5<=0) printf("Yes\n");
else printf("No\n");
}
return 0;
}
int solve(char a[],char b[],int n){
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(a[i-1]==b[j-1])
tb[i][j]=tb[i-1][j-1]+1;
else{
if(tb[i][j-1]>tb[i-1][j])
tb[i][j]=tb[i][j-1];
else tb[i][j]=tb[i-1][j];
}
}
return tb[n][n];
}
E 出列人数
(很抱歉了,没有第5题,学校oj没开,但是题目其实就是先把最长递增子序列求出来,然后用总数去减即可)
F XP的午餐
题目描述
XP每天都会思考一个问题,今天午餐去哪里吃?这是一个很重要的问题,这会影响到他下午的体力值。他的午餐预算是M元,现在有N种菜品,每一种菜品的价格和能够提供的体力值已知(每种菜品只能选择一次),请问如何选择菜品能够让XP下午的体力值最大呢?
输入
多组输入
第一行:M元和菜品数量N。
接下来N行,每一行两个整数,分别表示每一种菜品的价格(vi)和能够获得的体力值(wi)。
(0<N<=20,0<=M<=1000)(0<=vi<=50,0<=wi<=100)
输出
最大体力值。
样例输入 Copy
10 5
1 5
2 4
3 3
4 2
5 1
样例输出 Copy
14
分析:这题其实就是一个变相的0-1背包问题, 用刚刚好的钱,获得最大的体力值。
只需在之前的0-1背包上,把v[i]与w [i]的功能换一下,c变成m即可。
这里就不作详细分析啦
请直接看代码分析:
代码实现:c语言
#include <stdio.h>
#include <stdlib.h>
int tb[2000][2000];
int main (){
int v[2000];
int w[2000];
int m;
int n;
while(~scanf("%d %d",&m,&n)){
for(int i=0;i<n;i++)
scanf("%d %d",&v[i],&w[i]);
int z=solve(v,w,m,n);
printf("%d",z);
printf("\n");
}
return 0;
}
int solve(int v[],int w[],int m,int n){
int jmax=min(m,v[n-1]-1);
for(int j=0;j<=jmax;j++)
tb[n-1][j]=0;
for(int j=jmax+1;j<=m;j++)
tb[n-1][j]=w[n-1];
for(int i=n-2;i>=0;i--)
{
int jmax=min(m,v[i]-1);
for(int j=0;j<=jmax;j++)
tb[i][j]=tb[i+1][j];
for(int j=jmax+1;j<=m;j++)
tb[i][j]=max(tb[i+1][j],tb[i+1][j-v[i]]+w[i]);
}
return tb[0][m];
}
int max(int x,int y){
return x>y?x:y;
}
int min(int x,int y){
return x<y?x:y;
}