算法题目练习

90 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

蓝桥杯练习038

数字三角形

题目链接:1163 -- The Triangle (poj.org)

The Triangle

Time Limit: 1000MSMemory Limit: 10000K

Description

7
3   8
8   1   0
2   7   4   4
4   5   2   6   5

(Figure 1)
Figure 1 shows a number triangle. Write a program that 
calculates the highest sum of numbers passed on a route that 
starts at the top and ends somewhere on the base.
 Each step can go either diagonally down to the left
  or diagonally down to the right.
图 1 显示了一个数字三角形。编写一个程序,
用于计算从顶部开始并在基数上某处结束的路线上传递的最高数字总和。
每个步骤都可以沿对角线向下向左或向对角向下向下向右。

Input

Your program is to read from standard input. 
The first line contains one integer N: 
the number of rows in the triangle.
 The following N lines describe the data of the triangle.
  The number of rows in the triangle is > 1 but <= 100. 
  The numbers in the triangle, all integers,
   are between 0 and 99.
您的程序是从标准输入读取的。第一行包含一个整数 N:
三角形中的行数。以下 N 条线描述了三角形的数据。
三角形中的行数> 1,但< = 100。
三角形中的数字,所有整数,都在0和99之间。

Output

Your program is to write to standard output. 
The highest sum is written as an integer.
您的程序是写入标准输出。最高和写为整数。

Sample Input

5
7
3 8
8 1 0 
2 7 4 4
4 5 2 6 5

Sample Output

30

解题思路

1.利用递归求解本问题
	1.1当到最后一行时直接返回
	1.2其他情况递归求左分支最大值与右分支的最大值
	1.3返回当前位置与左右分支最大值的和
	1.4左分支是同一列,右分支是下一列
2.记忆性递归
	2.1用额外的空间存储数据
	2.2当前值存放于value中,对应输入的(i,j)位置
	2.3最后一行就是value值,其他行的情况:
		2.3.1 辅助空间全部初始化为0,v1map数组的i+1j列数据,v2map数组i+1j+1列数据,也就是v1的右侧的数
		2.3.2v1v20时,递归求解
		2.3.3最后的值为valuev1v2最大值的和
		2.3.4储存value值到map数组中并返回value
3.DP
	3.1dp数组求解,dp数组的头部就是最后结果,从下向上累加,加到最后便是最后的值。
	3.2先初始化dp数组最后一行
		3.2.1最后一行就是triangle数组的最后一行
		3.2.2循环求出dp数组剩余数据,从下向上求解
		3.3.3dp[k][l]triangle[k][l]与左右两分支最大值之和。

代码

#include<iostream>
using namespace std;
//DP 
int maxSumUsingDp(int **triangle,int i,int j,int n){
	int rowCount=n;
	int columnCount=n+1;
	int **dp=new int*[rowCount];
	for(int i=0;i<rowCount;i++)
		dp[i]=new int[columnCount];
	for(int k=0;k<columnCount;k++){
		//初始化最后一行 
		dp[rowCount-1][k]=triangle[rowCount-1][k];
	}
	for(int k=rowCount-2;k>=0;k--){
		for(int l=0;l<=k;l++){
			dp[k][l]=triangle[k][l]+
			max(dp[k+1][l],dp[k+1][l+1]);
		}
	}
	return dp[0][0];
	
} 
//DP
int maxSumDp(int **triangle,int n,int i,int j){
	int rowCnt=n;
	int colCnt=n+1;
	int *dp=new int[colCnt];
	for(int k=0;k<colCnt;k++){
		dp[k]=triangle[rowCnt-1][k];
	}
	for(int k=rowCnt-2;k>=0;k--){
		for(int l=0;l<=k;l++){
			dp[l]=triangle[k][l]
			+max(dp[l],dp[l+1]);
		}
	}
	return dp[0];
} 


int maxSumUsingRecursive(int **triangle,int n,int i,int j){
	int rowIndex=n;
	if(i==rowIndex-1){
		return triangle[i][j];
	}else{
		//顶点的值+max(左侧分支最大值,右侧分支最大值) 
		return triangle[i][j]+
		max(maxSumUsingRecursive(triangle,n,i+1,j),
		maxSumUsingRecursive(triangle,n,i+1,j+1));
	}
}

//记忆性递归
int maxSumUsingMemory(int **triangle,int n,int i,int j,int **map){
	int rowIndex=n;
	int value=triangle[i][j];
	if(i==rowIndex-1){
		
	}else{
		int v1=map[i+1][j];
		if(v1==0){
			v1=maxSumUsingMemory(triangle,n,i+1,j,map);
		}
		int v2=map[i+1][j+1];
		if(v2==0){
			v2=maxSumUsingMemory(triangle,n,i+1,j+1,map);
		}
		value=value+max(v1,v2);
	}
	map[i][j]=value;
	return value;
} 
int main()
{
	int n;
	cin>>n;
	int **triangle=new int*[n];
	int **map=new int*[n];
	for(int i=0;i<n;i++)
		map[i]=new int[i+1];
	for(int i=0;i<n;i++){
		for(int j=0;j<i+1;j++)
			map[i][j]=0;
	}
	for(int i=0;i<n;i++){
		triangle[i]=new int[i+1];
		for(int j=0;j<i+1;j++){
			cin>>triangle[i][j];
		}
	}
	cout<<maxSumDp(triangle,n,0,0)<<endl;
	cout<<maxSumUsingMemory(triangle,n,0,0,map)<<endl;
	cout<<maxSumUsingRecursive(triangle,n,0,0)<<endl;
	cout<<maxSumUsingDp(triangle,0,0,n)<<endl;
	return 0;
}

蓝桥杯练习039

Crossing River

题目链接:1700 -- Crossing River (poj.org)

Description

A group of N people wishes to go across a river with only 
one boat, which can at most carry two persons. 
Therefore some sort of shuttle arrangement must be arranged
 in order to row the boat back and forth so that all people 
 may cross. Each person has a different rowing speed; 
 the speed of a couple is determined by the speed of
  the slower one. Your job is to determine a strategy 
  that minimizes the time for these people to get across.
一群N个人希望只用一艘船过河,最多可以搭两个人。因此,必须安排某种穿梭安排,
以便来回划船,以便所有人都可以穿越。每个人都有不同的划船速度;
两人的速度是由较慢的人的速度决定的。你的工作是确定一个策略,
最大限度地减少这些人的过河时间。

Input

The first line of the input contains a single integer
 T (1 <= T <= 20), the number of test cases. 
 Then T cases follow. The first line of each case contains N,
  and the second line contains N integers giving the time 
  for each people to cross the river. Each case is preceded 
  by a blank line. There won't be more than 1000 people
   and nobody takes more than 100 seconds to cross.
输入的第一行包含单个整数 T1 <= T <= 20),即测试用例的数量。
然后是T个案例。每个案例的第一行包含 N,第二行包含 N 个整数,
给出了每个人过河的时间。每个事例前面都有一个空行。不会超过1000人,
没有人需要超过100秒才能通过。

Output

For each test case, print a line containing the total 
number of seconds required for all the N people
 to cross the river.
对于每个测试用例,打印一行,其中包含所有 N 个人过河所需的总秒数。

Sample Input

1
4
1 2 5 10

Sample Output

17

解题思路

1.首先想到的是让速度快的来回带人过河
2.有时候这样并不是最优的结果
3.换个思路,先将过河时间排序,然后让头两个最快的过河,然后最快的回来,
最慢的两个人过河,然后第二快的人过来,然后最快的和第三快的出发,1返回。。。

代码

#include<iostream>
#include<algorithm>
using namespace std;
int ans; 
void f(int n,int speed[]){
	int left=n;
	while(left>0){
		if(left==1){//只有1人 
			ans+=speed[0];
			break;
		}else if(left==2){//只有2人
			ans+=speed[1];
			break; 
		}else if(left==3){//只有3人
			ans+=speed[2]+speed[0]+speed[1];
			break; 
		}else{
			//1.2出发,1返回,最后两名出发,2返回
			int s1=speed[1]+speed[0]+speed[left-1]+speed[1];
			//1,3出发,1返回,1,4出发,1返回,1,2过河 
			int s2=speed[left-1]+speed[left-2]+2*speed[0];
			ans+=min(s1,s2);
			//左侧是渡河的起点,left代表左侧的剩余人数 
			left-=2; 
		}
	}
}


int main()
{
	int T;
	int n;
	cin>>T;
	for(int i=0;i<T;i++){
		cin>>n;
		int speed[n];
		for(int j=0;j<n;j++){
			cin>>speed[j];
		}
		sort(speed,speed+n);
		f(n,speed);
	}
	cout<<ans<<endl;
	return 0;
}

蓝桥杯练习040

区间调度

问题描述

有n项工作,每项工作分别在si时间开始,在ti时间结束
对于每项工作,你都可以选择参与与否,如果选择了参与,
那么自始至终都必须全程参与,此外,参与工作的时间段
不能重复(即使是开始的瞬间和结束的瞬间的重叠也是不允许的)
你的目标是参与尽可能多的工作,那么最多能参与多少项工作呢?
1<=n<=100000
1<=si<=ti<=10^9

输入:
第一行:n
第二行:n个整数空格隔开,代表n个工作的开始时间
第三行:n个整数空格隔开,代表n个工作的结束时间

样例输入:
5
1 2 4 6 8
3 5 7 9 10

样例输出:
3

解题思路

1.本题工作的开始时间和结束时间是一体的,利用结构体捆绑到一块
2.将本题转化为结构体排序,按照结束时间的早晚排序
3.自己写比较函数cmp,调用sort函数时将cmp传入即可
4.当结束时间相同时按开始时间的早晚排序

代码

#include<iostream>
#include<algorithm>
using namespace std;

struct Job{
	int s;//开始时间 
	int t;//结束时间 
};

bool cmp(Job job1,Job job2){
	if(job1.t==job2.t)//结束时间相同按开始时间早晚排序 
		return job1.s<job2.s;
	else//按结束时间早晚排序 
		return job1.t<job2.t; 
} 
//最多参与工作的个数 
int f(Job jobs[],int n){
	int cnt=1;//当前可以参与1个工作 
	int y=jobs[0].t;//记录第一个工作的结束时间 
	for(int i=0;i<n;i++){//对每个工作进行遍历 
		if(jobs[i].s>y){//当前工作的开始时间早于结束时间 
			cnt++; 
			y=jobs[i].t;//更新结束时间为当前工作的结束时间 
		}
	}
	return cnt;//返回最多参与工作的数目 
}
int main()
{
	int n;
	cin>>n;
	Job jobs[n];
	for(int i=0;i<n;i++)
		cin>>jobs[i].s;
	for(int i=0;i<n;i++)
		cin>>jobs[i].t;
	//排序 
	sort(jobs,jobs+n,cmp);
	int res=f(jobs,n);
	cout<<res<<endl;
	return 0;
}

蓝桥杯练习041

钢条切割

问题描述

Serling公司购买长钢条,将其切割为短钢条出售。切割工序本身没有成本支出。公司管理层希望知道最佳的切割方案。
假定我们知道Serling公司出售一段长为i英寸的钢条的价格为pi(i=1,2,…,单位为美元)。钢条的长度均为整英寸。

| 长度i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| - | - | - | - | - | - | - | - | - | - |
价格pi | 1 | 5 | 8 | 16 | 10 | 17 | 17 | 20 | 24 | 30 |

钢条切割问题是这样的:给定一段长度为n英寸的钢条和一个价格表pi(i=1,2,…n),求切割钢条方案,使得销售收益rn最大。
注意,如果长度为n英寸的钢条的价格pn足够大,最优解可能就是完全不需要切割

解题思路

1.用dp求解本问题
2.分别考虑钢条的长度
	2.1当钢条长1时,价格只有1
	2.2钢条长为2时,可以切割成1,1,也可以不切为2
		切割后价值为1+1=2,不切割为5,取最大值
	2.3长度为3时,分为1,2;2,1;3,0
		价值分别为:6,6,8,取最大值
	2.4长为n时,分为1,n-1;2,n-2;3,n-3;……;n,0;
		求得价值取最大值。

代码

#include<iostream>
using namespace std;


const int n=10;
int a[]={1,5,8,16,10,17,20,24,30}; 
int vs[n+1];
//递归函数 
int r(int x){//x 钢条的长度
	if(x==0)return 0;//出口 
	int ans=0;
	for(int i=1;i<=x;i++){
		if(vs[x-i]==-1){
			vs[x-i]=r(x-i);
		}
        //a是一整段,vs是对剩余部分切割
		int v=a[i-1]+vs[x-i];
		ans=max(v,ans);
	}
	vs[x]=ans;
	return ans;
} 
int dp(){
	vs[0]=0;
	//拥有的钢条长度 
	for(int i=1;i<=n;i++){
		//保留j为整段 
		for(int j=1;j<=i;j++){
			vs[i]=max(a[j-1]+vs[i-j],vs[i]);
		}
	}
	return vs[n];
}
int main()
{
	for(int i=0;i<n+1;i++)
		vs[i]=-1;
	cout<<r(n)<<endl;
	for(int i=0;i<n+1;i++)
		vs[i]=0;
	cout<<dp()<<endl;
	return 0;
}

蓝桥杯练习042

最长递增子序列

问题描述

找到给定序列的最长子序列的长度,使得子序列所有元素单调递增。

输入输出

输入:4 2 3 1 5 
输出 3(因为2 3 5 组成了最长递增子序列) 

代码

#include<iostream>
using namespace std;

//最长递增子序列的长度
//输入:4 2 3 1 5 
//输出 3(因为2 3 5 组成了最长递增子序列) 
int f(int arr[],int n){
	int maxCnt=0;//最大值
	for(int i=0;i<n;i++){
		int p=i;
		int cnt=1;//计数
		for(int j=i+1;j<n;j++){
			if(arr[j]>arr[p]){
				cnt++;
				p=j;
			}
		}
		maxCnt=max(maxCnt,cnt);//更新
	}
	return maxCnt;
} 

int main()
{
	int arr[]={4,2,3,1,5};
	cout<<f(arr,5)<<endl; 
	return 0;
}