SDU程序设计思维与实践作业Week10

A签到题

题目

题目
input&&output
input&&output
Sample

    #Simple Input 1
    120 51840
    #Simple Output 1
    7
    #Simple Input 2
    42 42
    #Simple Output 2
    0
    #Simple Input 3
    48 72
    #Simple Output 3
    -1

题解

本题直接暴力就可以了

C++代码

#include<iostream>
#include<cmath>

using namespace std;

int main(){
	int n,m;
	cin>>n>>m;
	if(m%n != 0) cout<<"-1"<<endl;
	else{
		int k=m/n;
		int num=0;
		while(k%2==0) {
			k/=2;
			num++;
		}
		while(k%3==0){
			k/=3;
			num++;
		}
		if(k == 1) cout<<num<<endl;
		else cout<<"-1"<<endl;
	}
	return 0;
}

BLIS & LCS

题目

题目
input&&output
input&&output
Sample

#input:
5 5
1 3 2 5 4
2 4 3 1 5
#output:
3 2

题解

1.LIS:最长上升子序列(严格递增)
	我们只需要记录0~i的最长上升子序列就可以了
	计算i+1时  若 存在j<i+1 那么我们就将他的标记矩阵(arr)即为max(arr[j])
	最后再假设i+1本身
2.LCS:最长公共子序列
	起初我想到将LCS转化为LIS(我们只要记录 A的元素在B中出现的位置然后求LIS就可以了,后来发现这种直接转化的方法对于A的元素在B中多次出现不太好处理(需要全部记录然后寻找最大LIS,因此换用另一种方法)
	由于涉及到两个数组并且索引位置各不相同,一维不足以记录变化状态,因此我们采用二维数组作为标记矩阵,f[i][j] 标识 Ai、Bj之前最长公共子序列,那么判断 i+1或j+1时若相同则为f[i][j]+1、不同此时有两种种情况 f[i][j-1]、f[i-1][j]由于ij位置不可放因此为二者的最大值,最终LCS即为f[m][n] (A大小为m B为m)

C++代码

#include<iostream>

using namespace std;
const int maxn = 1e4;
long long A[maxn],B[maxn];
long long mark[maxn];
long long C[maxn][maxn];//存储 A中元素在B中出现的位置

void init(int n,long long * arr){
	for(int i=0;i<n;i++) arr[i] = 0;
}
void LIS(int n,long long *arr){
	//最长上升子序列  严格递增
	for(int i=0;i<n;i++){
		for(int j=0;j<i;j++)
			if(arr[j] < arr[i] && mark[j] > mark[i])
				mark[i] = mark[j];		
		mark[i]++;
	} 
}

void LCS(int n,int m){
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++)
			C[i][j] = 0;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++){
			if(A[i-1] == B[j-1]) C[i][j] = C[i-1][j-1] + 1;
			else C[i][j] = max(C[i][j-1],C[i-1][j]);
		}
} 
int main(){
	int m,n;
	long long ma=0;
	cin>>n>>m;
	init(n,A);
	init(m,B);
	init(n,mark);
	for(int i=0;i<n;i++) cin>>A[i];
	for(int i = 0;i<m;i++) cin>>B[i];
	LIS(n,A);
	for(int i=0;i<n;i++) if(mark[i]>ma) ma = mark[i];
	cout<<ma<<" ";
	init(n,mark);
	LCS(n,m);
	cout<<C[n][m]<<endl;
	return 0;
}

C

题目

题目
input&&output
input&&output
Sample

    #Input
    2
    1 2
    #Output
    2
    #Input
    3
    1 2 3
    #Output
    4
    #Input
    9
    1 2 1 3 2 2 2 2 3
    #Output
    10

题解

这是一道拿数问题的变种(DP解决)
实际上仅仅是吧每个数的个数增加了,因此我们更新时增加的数不再是数字本身而是数字*个数
拿数问题:
	要从一组数中拿数,但拿完之后x-1、x+1均不可取,我们构建dp[i]为i拿数后的最大值,那么i+1如何取,i+1有两种可能,若i被取了那么i+1不可取,若i-1被取了那么i可取那么dp[i]为二者的较大值(只不过此处若取i增加的数量为  x*num_x

C++代码

#include<iostream>

using namespace std;

const int maxn = 1e6+500;
long long num[maxn],dp[maxn],ele[maxn];
long long mx = -1,mn = maxn,curr;
int main(){
	int n;
	cin>>n;
	for(int i=0;i<n+1;i++) {num[i] = 0;dp[i] = 0;ele[i]=0;}
	for(int i=0;i<n;i++){
		cin>>curr;
		mx = max(mx,curr);
		mn = min(mn,curr);
		num[curr]++;
	}

	for(int i=mn;i<=mx;i++)
		if(i==mn) dp[i] = i*num[i];
		else if(i==mn+1) dp[i] = max(dp[i-1],i*num[i]);
		else dp[i] = max(dp[i-1],dp[i-2] + i*num[i]);
	cout<<dp[mx]<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值