2024年春季学期《算法分析与设计》练习10

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
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e3 +5;
const int inf =0x3fffffff;
void solve(){
    int n;
    while(cin>>n){
    vector<int> a(n+1);
    vector<int> dp(n+1);
    dp[0]=0;
    int len=1;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        dp[i]=inf;
    }
    dp[1]=a[1];
    for(int i=2;i<=n;i++){
        int l=0,r=len;
        if(a[i]>dp[len]){
            dp[++len]=a[i];
        }
        else{
            while(l<r){
                int mid=(l+r)>>1;
                if(dp[mid]>=a[i]){
                    r=mid;
                }
                else{
                    l=mid+1;
                }
            }
            dp[l]=min(a[i],dp[l]);
        }
    }
    cout<<len<<"\n";
}
}
int main() {
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    solve();
    return 0;
}

 B:构造最长递增子序列

题目描述

在“最长递增子序列”的基础上对代码进行改进,输出一条最长递增子序列。

输入
每组输入包括两行,第一行为序列长度n,第二行为序列。
输出
输出最长递增子序列中的任意一条即可。
样例输入 Copy
7
1 7 3 4 9 2 3
样例输出 Copy
1 3 4 9
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N =1e2 +5;
const int M =1e9 +7;
const int inf =0x3fffffff;
int a[N],b[N],c[N],d[N];
void solve(){
    int n;
    while(cin>>n){
        memset(a,0,sizeof a);
        memset(b,0,sizeof b);
        memset(c,0,sizeof c);
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        b[1]=1;
        int m=0;
        for(int i=2;i<=n;i++){
            m=0;
            for(int j=i-1;j>=1;j--){
                if(a[j]<a[i]&&b[j]>m){
                    m=b[j];
                    d[i]=j;
                }
            }
            b[i]=m+1;
        }
        m=b[1];
        int s=1;
        for(int i=2;i<=n;i++){
            if(b[i]>m){
                m=b[i];
                s=i;
            }
        }
        int i=s;
        int j=m;
        while(j){
            c[j--]=a[i];
            i=d[i];
        }
        for(int i=1;i<=m;i++){
            cout<<c[i]<<" \n"[i==n-1];
        }
    }
}
int main() {
    cin.tie(0)->sync_with_stdio(false);
    solve();
    return 0;
}

 C:出列人数

题目描述

有N位同学站在一排,体育老师要请其中的(N-K)位同学出列,将剩下的K位同学从左到右依次编号为1,2,3,…K,他们的身高分别为T1,T2,T3,…TK,要求满足T1<T2<T3<…<TK。已知N位同学的身高,请设计一个算法,计算最少需要几位同学出列可使得剩下的同学满足上述要求。

输入

多组输入,对于每一组测试数据,第1行N表示同学数量(n<=1000)。
第2行包含N个正整数,分别表示每一个同学的身高。(单位:厘米)

输出

输出最少需要出列的同学人数。

样例输入 Copy
4
172 185 169 178
5
165 168 174 170 182
样例输出 Copy
2
1
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N =1e3 +5;
const int M =1e9 +7;
const int inf =0x3fffffff;
int a[N],dp[N];
void solve(){
    int n;
    while(cin>>n){
        int len=1;
        for(int i=1;i<=n;i++){
            cin>>a[i];
            dp[i]=inf;
        }
        dp[1]=a[1];
        for(int i=2;i<=n;i++){
            int l=0,r=len;
            if(a[i]>dp[len]){
                dp[++len]=a[i];
            }
            else{
                while(l<r){
                    int mid=(l+r)>>1;
                    if(dp[mid]>=a[i]){
                        r=mid;
                    }
                    else{
                        l=mid+1;
                    }
                }
                dp[l]=min(a[i],dp[l]);
            }
        }
        cout<<n-len<<"\n";
    }
}
int main() {
    cin.tie(0)->sync_with_stdio(false);
    solve();
    return 0;
}

 D: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
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 5;
const int P = 131;
int dx[] = {0, 0, -1, 1};
int dy[] = {1, -1, 0, 0};
int w[N], v[N];
int mem[N][N];
int n, c;
int f[N];
void dfs(int x, int y)
{
    if (x <=n)
    {
        if (mem[x + 1][y] == mem[x][y])
        {
            f[x] = 0;
            dfs(x + 1, y);
        }
        else if (y - v[x] >= 0 && mem[x][y] == mem[x + 1][y - v[x]] + w[x])
        {
            f[x] = 1;
            dfs(x + 1, y - v[x]);
        }
    }
}
void solve()
{
    cin >> n >> c;
    for (int i = 1; i <= n; i++)
        cin >> w[i];
    for (int i = 1; i <= n; i++)
        cin >> v[i];
    int sum = 0;
    for (int i = n; i >= 1; i--)
        for (int j = 1; j <= c; j++)
        {
            if (j < v[i])
                mem[i][j] = mem[i + 1][j];
            else
                mem[i][j] = max(mem[i + 1][j], mem[i + 1][j - v[i]] + w[i]);
        }
    cout << mem[1][c] << "\n";
    dfs(1, c);
    for (int i = 1; i <= n; i++)
        cout << f[i];
    cout << "\n";
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    solve();
    return 0;
}

 E: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
#include <bits/stdc++.h>
#define ll long long
#define MM 0x3f3f3f3f
using namespace std;
const int N = 1e3 + 5;
const int P = 131;
int v[N], w[N];
int dp[N][N];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int m, n;
    while (cin >> m >> n)
    {
        for (int i = 1; i <= n; i++)
            cin >> v[i] >> w[i];
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
                if (v[i] <= j)
                    dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - v[i]] + w[i]);
                else
                    dp[i][j] = dp[i - 1][j];
        cout << dp[n][m] << "\n";
    }
    return 0;
}

 F:补充能量

题目描述

一年一度的宇宙超级运动会在宇宙奥特英雄体育场隆重举行。X星人为这场运动会准备了很长时间,他大显身手的时刻终于到了!
为了保持良好的竞技状态和充沛的体能,X星人准备了N个不同的能量包,每个能量包都有一个重量值和能量值。由于这些能量包的特殊性,必须要完整地使用一个能量包才能够发挥功效,否则将失去能量值。
考虑到竞赛的公平性,竞赛组委会规定每个人赛前补充的能量包的总重量不能超过W。
现在需要你编写一个程序计算出X星人能够拥有的最大能量值是多少?

输入

单组输入。
第1行包含两个正整数N和W,其中N<=103,W<=103。
第2行包含N个正整数,分别表示每一个能量包的重量,两两之间用空格隔开。
第3行包含N个正整数,分别表示每一个能量包的能量值,两两之间用空格隔开。

输出

输出X星人能够拥有的最大能量值。

样例输入 Copy
3 10
4 5 7
100 120 200
样例输出 Copy
220
#include <bits/stdc++.h>
#define ll long long
#define MM 0x3f3f3f3f
using namespace std;
const int N = 1e3 + 5;
const int P = 131;
const int MOD=1e9+7;
const double PI = acos(-1.0);
int a[N],b[N],c[N];
void solve(){
    int n,w;
    cin>>n>>w;
    for(int i=0;i<n;i++){
        cin>>a[i];
    }
    for(int i=0;i<n;i++){
        cin>>b[i];
    }
    for(int i=0;i<n;i++){
        for(int j=w;j>=a[i];j--){
            c[j]=max(c[j],c[j-a[i]]+b[i]);
        }
    }
    cout<<c[w]<<'\n';
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    solve();
    return 0;
}

 G:道具的魅力值

题目描述

在某网络游戏中提供了一个道具库,在道具库中每种道具均有若干件(数量已知),游戏玩家购买一件道具将获得一定的魅力值。
已知每种道具的价格和魅力值,请编写一个程序,在总价格不超过某个上限的情况下使得所购道具的魅力值之和达到最大。

输入

每组测试数据的输入有n+1行,n表示道具的种类。(n<=100,p<=10000)
第1行包含两个正整数,分别表示道具种类数n和总价值的上限p,两个数字之间用空格隔开。
第2行到第n+1行分别对应于第1种道具到第n种道具的信息,每1行包含三个正整数,两个数字之间用空格隔开,三个正整数分别表示某一种道具的数量、单个道具的价格和魅力值。

输出

每组测试数据的输出只有一行,即道具魅力值的最大和。

样例输入 Copy
3 10
2 2 3
1 5 10
2 4 12
样例输出 Copy
27
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N =1e3 +5;
const int M =1e9 +7;
struct S{
    int u,v,w;
}a[105];
bool cmp(S x,S y){
    return (double)x.w/x.v>(double)y.w/y.v;
}
void solve(){
    int n,p;
    while(cin>>n>>p){
        for(int i=0;i<n;i++){
            cin>>a[i].u>>a[i].v>>a[i].w;
        }
        sort(a,a+n,cmp);
        int s=0;
        for(int i=0;i<n;i++){
            int ss=min(a[i].u,p/a[i].v);
            s+=ss*a[i].w;
            p-=ss*a[i].v;
        }
        cout<<s<<"\n";
    }
}
int main() {
    cin.tie(0)->sync_with_stdio(false);
    solve();
    return 0;
}

 H:最长不下降子序列(还不会)

题目描述

给定一个序列 a,求去除 a 中一段连续长度为 L 的序列后,a 的最长不下降子序列的长度的最大值。

输入

单组数据。
第一行两个整数 n,L 表示序列的长度为 n,L 如题意所示。
第二行 n 个数表示序列 a
n ≤ 105, 0 ≤ L ≤ n

输出

输出一个整数表示最长不下降子序列长度的最大值

样例输入 Copy
6 3
2 1 3 6 4 5
样例输出 Copy
3

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值