PTA 动态规划

   文章目录

一、函数题

二、编程题


一、编程题

1.租用游艇问题

长江游艇俱乐部在长江上设置了n个游艇出租站1,2,…,n。游客可在这些游艇出租站租用游艇,并在下游的任何一个游艇出租站归还游艇。游艇出租站i到游艇出租站j之间的租金为r(i,j),1<=i<j<=n。试设计一个算法,计算出从游艇出租站1 到游艇出租站n所需的最少租金。

输入格式:

第1 行中有1 个正整数n(n<=200),表示有n个游艇出租站。接下来的第1到第n-1 行,第i行表示第i站到第i+1站,第i+2站, ... , 第n站的租金。

输出格式:

输出从游艇出租站1 到游艇出租站n所需的最少租金。

#include<bits/stdc++.h>
using namespace std;
int a[500][500];
int dp[500];
int main()
{
    int n;cin>>n;
    for(int i=1;i<n;i++)
        for(int j=i+1;j<=n;j++)
            cin>>a[i][j];
    memset(dp,0x3f,sizeof dp);
    dp[1]=0;
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
            dp[j]=min(dp[j],dp[i]+a[i][j]);
    cout<<dp[n]<<endl;
}

2.矩阵链相乘问题

矩阵的乘法定义如下:设A是m×p的矩阵,B是p×n的矩阵,则A与B的乘积为m×n的矩阵,记作C=AB,其中,矩阵C中的第i行第j列元素cij​可以表示为:cij​=Σk=1p​aik​×bkj​=ai1​b1j​+ai2​b2j​+⋯+aip​bpj​.

当多个矩阵相乘时,采用不同的计算顺序所需的乘法次数不相同。例如,A是50×10的矩阵,B是10×20的矩阵,C是20×5的矩阵,
计算ABC有两种方式:(AB)C和A(BC),前一种需要15000次乘法计算,后一种则只需3500次。

设A1​,A2​,...,An​为矩阵序列,Ai​是阶为Pi−1​∗Pi​的矩阵(1≤i≤n)。试确定矩阵的乘法顺序,使得计算A1​A2​...An​过程中元素相乘的总次数最少。

输入格式:

每个输入文件为一个测试用例,每个测试用例的第一行给出一个正整数n(1≤n≤100),表示一共有n个矩阵A1​,A2​,...,An​,第二行给出n+1个整数P0​,P1​...Pn​,以空格分隔,其中1≤Pi​≤100(0≤i≤n),第i个矩阵Ai​是阶为Pi−1​∗Pi​的矩阵。

输出格式:

获得上述矩阵的乘积,所需的最少乘法次数

#include<bits/stdc++.h>
using namespace std;
const int N=2000;
int dp[N][N];
int a[N];
int main()
{
    int n;cin>>n;
    memset(dp,0x3f,sizeof dp);
    for(int i=0;i<=n;i++)cin>>a[i],dp[i][i]=0;
    for(int len=2;len<=n;len++)
    {
        for(int l=1;l+len-1<=n;l++)
        {
            int r=l+len-1;
            for(int k=l;k<r;k++)
                dp[l][r]=min(dp[l][r],dp[l][k]+dp[k+1][r]+a[l-1]*a[k]*a[r]);
        }
    }
    cout<<dp[1][n]<<endl;
}

3.字母表

我们定义一个小写字符串是“按字母表的”,当且仅当它删除掉一些字符后,可以变为”abcdefghijklmnopqrstuvwxyz”。
给定一个长度为n的小写字母字符串,至少插入多少个字符才能使其变成“按字母表的”。

输入格式:

输入第一行为一个整数T(1<=T<=100),代表测试数据的组数,随后T行,每行都是由小写字母'a'-'z'组成的一串字符s (1=<|n|<=50)。

输出格式:

输出为了让s变成“按字母表的”,至少要插入的字符个数,每组输出占一行。

#include<bits/stdc++.h>
using namespace std;
int dp[100];
int main()
{
    int t;cin>>t;
    while(t--)
    {
        string s;cin>>s;int n=s.size();
        int a[100]={0};
        for(int i=0;i<100;i++)dp[i]=1;
        for(int i=0;i<n;i++)a[i+1]=s[i]-'a';
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<i;j++)
            {
                if(a[i]>a[j])dp[i]=max(dp[i],dp[j]+1);
            }
        }
        int ans=0;
        for(int i=1;i<=n;i++)ans=max(ans,dp[i]);
        cout<<26-ans<<endl;
    }
}

4.小H分糖果

小H来到一个小学分糖果,小学生们很听话,站成一排等着分糖果,小H将根据每个人的上次考试分数给一定的糖果,规则如下。

  • 每个人都有自己分数ai​,代表上次考试成绩。
  • 每个人都至少有一颗糖。
  • 如果两个人相邻,分数高的一定比分数低的糖果多

然而小H经费有限,他想知道最少需要多少糖果。

输入格式:

输入第一行包括一个整数n(1≤n≤105)。
接下来n行,每行1个整数ai​(1≤ai​≤105)表示站在第i位的人的分数。

输出格式:

输出最少需要多少糖果

#include<bits/stdc++.h>
using namespace std;
const int N=2e5;
int a[N],b[N];
int main()
{
    int n;cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<n;i++)
    {
        if(a[i+1]>a[i])b[i+1]=b[i]+1;
    }
    for(int i=n;i>1;i--)
    {
        if(a[i-1]>a[i])b[i-1]=max(b[i-1],b[i]+1);
    }
    long long ans=0;
    for(int i=1;i<=n;i++)
        ans+=b[i];
    cout<<ans+n<<endl;
}

5.运动会

T公司的员工层级关系可以表示成一棵树,员工X是员工Y的直接领导,则在树中X是Y的父结点。公司拟组织一场运动会,但为了避免尴尬,每个员工都不想与自己的直接领导一起参赛。假定每个员工都对应一个权重(领导的权重不一定比下属大),请你编写程序,邀请若干员工参赛,使得参赛人员的总权重和最大。

输入格式:

第一行一个正整数n,表示公司的员工人数,员工编号为1...n,n不超过3000。
接下来n行,每行1个整数,表示每个员工的权重,值域为[−27,27)。
接下来n-1行,每行为两个空格间隔的整数X和Y,表示Y是X的直接领导。

输出格式:

输出为一个整数,表示参赛员工的最大权重之和。

#include<bits/stdc++.h>
using namespace std;
const int N=3010;
vector<int> a[N];
int root=0,w[N],b[N];
int no[N],yes[N];
void dfs(int u)
{
    if(a[u].empty())
    {
        yes[u]=w[u];
        return;
    }yes[u]=w[u];
    for(int i=0;i<a[u].size();i++)
    {
        dfs(a[u][i]);
        yes[u]+=no[a[u][i]];
        no[u]+=max(yes[a[u][i]],no[a[u][i]]);
    }
}
int main()
{
    int n;cin>>n;
    for(int i=1;i<=n;i++)cin>>w[i];
    for(int i=1;i<n;i++)
    {
        int x,y;cin>>x>>y;
        a[y].push_back(x);b[x]=y;
    }
    for(int i=1;i<=n;i++)
    {
        if(b[i]==0)
            root=i;
    }
    //cout<<root<<endl;
    dfs(root);
    cout<<max(no[root],yes[root]);
}

6.0-1背包

给定n(n<=100)种物品和一个背包。物品i的重量是wi(wi<=100),价值为vi(vi<=100),背包的容量为C(C<=1000)。
应如何选择装入背包中的物品,使得装入背包中物品的总价值最大? 在选择装入背包的物品时,对每种物品i只有两个选择:装入或不装入。不能将物品i装入多次,也不能只装入部分物品i。

输入格式:

共有n+1行输入:
第一行为n值和c值,表示n件物品和背包容量c;
接下来的n行,每行有两个数据,分别表示第i(1≤i≤n)件物品的重量和价值。

输出格式:

输出装入背包中物品的最大总价值。

#include<bits/stdc++.h>
using namespace std;
int w[200],v[200],dp[1010];
int main()
{
    int n,c;cin>>n>>c;
    for(int i=1;i<=n;i++)cin>>w[i]>>v[i];
    for(int i=1;i<=n;i++)
        for(int j=c;j>=w[i];j--)
            dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    cout<<dp[c];
}

7.最长公共子序列长度

求两个字符串的最长公共子序列长度。

输入格式:

输入长度≤100的两个字符串。

输出格式:

输出两个字符串的最长公共子序列长度。

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int dp[N][N];
int main()
{
    string a,b;cin>>a>>b;
    a=" "+a;b=" "+b;
    for(int i=1;i<a.size();i++)
    {
        for(int j=1;j<b.size();j++)
        {
            dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
            if(a[i]==b[j])dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
        }
    }
    cout<<dp[a.size()-1][b.size()-1];
}

8.jmu-ds-最长公共子串

给出2个字符串,输出2字符串的最长公共子串。

输入格式:

输入2个字符串,不可包含空格。

输出格式:

输出2个字符串的最长公共子串。若没有公共子串,则输出“NULL”

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int dp[N][N];
int main()
{
    string a,b;cin>>a>>b;
    a=" "+a;b=" "+b;
    int len=0,end;
    for(int i=1;i<a.size();i++)
    {
        for(int j=1;j<b.size();j++)
        {
            if(a[i]==b[j])
            {
                dp[i][j]=dp[i-1][j-1]+1;
                if(dp[i][j]>len)
                    len=dp[i][j],end=i;
            }
        }
    }
    if(len)
    {
        for(int i=end-len+1;i<=end;i++)cout<<a[i];
    }
    else cout<<"NULL";
}

9.石子合并

在一个圆形操场的四周摆放 N 堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的 2 堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

试设计出一个算法,计算出将 N 堆石子合并成 1 堆的最小得分和最大得分。

输入格式:

数据的第 1 行是正整数 N ,表示有 N 堆石子。

第 2 行有 N 个整数,第 i 个整数 ai​ 表示第 i 堆石子的个数。

输出格式:

输出共 2 行,第 1 行为最小得分,第 2 行为最大得分。

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int dp1[N][N],dp2[N][N],a[N],s[N];
int main()
{
    int n;cin>>n;
    memset(dp1,0x3f,sizeof dp1);
    memset(dp2,-1,sizeof dp2);
    for(int i=1;i<=n;i++)cin>>a[i],a[i+n]=a[i];
    n=2*n-1;
    for(int i=1;i<=n;i++)
    {
        dp1[i][i]=dp2[i][i]=0;
        s[i]=s[i-1]+a[i];
    }
    for(int len=2;len<=n;len++)
        for(int l=1;l<=n-len+1;l++)
        {
            int r=l+len-1;
            for(int k=l;k<r;k++)
            {
                dp1[l][r]=min(dp1[l][r],dp1[l][k]+dp1[k+1][r]);
                dp2[l][r]=max(dp2[l][r],dp2[l][k]+dp2[k+1][r]);
            }
            dp1[l][r]+=s[r]-s[l-1];
            dp2[l][r]+=s[r]-s[l-1];
        }
    n=(n+1)/2;
    int mi=0x3f3f3f3f,ma=-1;
    for(int i=1;i<=n;i++)
    {
        mi=min(mi,dp1[i][n+i-1]);
        ma=max(ma,dp2[i][n+i-1]);
    }
    cout<<mi<<endl;
    cout<<ma<<endl;
}

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值