蓝桥杯训练-3.21

蓝桥杯训练-3.21

代码练习

• 一元三次方程求解

问题描述

有形如:ax3+bx2+cx+d=0 这样的一个一元三次方程。给出该方程中各项的系数(a,b,c,d 均为实数),并约定该方程存在三个不同实根(根的范围在-100至100之间),且根与根之差的绝对值>=1。要求三个实根。。

输入格式

四个实数:a,b,c,d

输出格式

由小到大依次在同一行输出这三个实根(根与根之间留有空格),并精确到小数点后2位

样例输入

1 -5 -4 20

样例输出

-2.00 2.00 5.00

数据规模和约定

|a|,|b|,|c|,|d|<=10

思路:

①暴力枚举:

因为数据规模小,可以在给定的范围内枚举,题目中约定了本题方程的根的范围在-100至100之间,需要我们输出的实根需精确到小数点后两位,所以我们在枚举的时候每次加0.01,同时我们方程的解并不是真正等于0的,而是有一定误差的,这个 误差只要不能超过|0.001|,就是准确的。

代码:

#include <bits/stdc++.h>
using namespace std;
const double e = 0.01;//因为本题根的范围在-100至100之间,并精确到小数点后两位,所以每次枚举的时候加0.01即可
int main()
{
    double a, b, c, d;
    scanf("%lf%lf%lf%lf",& a, & b, & c, & d);
    for(double x = -100.00; x <= 100.00; x += e)
    {
        double f = x * x * x * a + x * x * b + x * c + d;
        if (f >= (-1)* (0.001) && f <= (0.001))//需要考虑方程解出来并不是真正等于0的,而是有一定误差的,这个 误差不能超过|0.001|
            printf("%.2lf ", x);
    }
    return 0;
}

② 牛顿迭代法:

在这里插入图片描述

先对函数求导,f’(x)=3ax^2+2*bx+c.

然后直接求根公式求f’(x)=0的点,也就是函数极点.

(我们可以顺便求一下凸形函数极值hhh)

这题保证有三个不定根,所以有两个单峰.

我们分别设这两个点为p,q.

然后显然的必有三个根在[-100,p),[p,q],(q,100]三个区间内 (两极点间必定存在零点,勘根定理).

代码:

#include <bits/stdc++.h>
using namespace std;
#define eps 1e-4
double a, b, c, d;
double x1, x2, x3;//方程的三个根
double f(double x)//原先的方程
{
    double y = x * x * x * a + x * x * b + x * c + d;
    return y;
}
double df(double x)//求完导以后的函数
{
    double y = 3 * x * x * a + 2 * x * b + c;
    return y;
}
double Newton(double l, double r)
{
    double x, x0 = (l + r) / 2;//开始时设的近似值就取区间中间的
    while(abs(x - x0) > eps)
    {
        x = x0 - f(x0) / df(x0);
        swap(x0, x);
    }
    return x0;
}
int main()
{
    cin >> a >> b >> c >> d;
    double p = (-b - sqrt(b * b - 3 * a * c)) / (3 * a);
    double q = (-b + sqrt(b * b - 3 * a * c)) / (3 * a);
    x1 = Newton(-100, p);
    x2 = Newton(p, q);
    x3 = Newton(q, 100);
    printf("%.2lf %.2lf %.2lf",x1,x2,x3);
    return 0;
}
• 入学考试-01背包-DP

问题描述

辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
  如果你是辰辰,你能完成这个任务吗?

输入格式

第一行有两个整数T(1 <= T <= 1000)和M(1 <= M <= 100),用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。接下来的M行每行包括两个在1到100之间(包括1和100)的整数,分别表示采摘某株草药的时间和这株草药的价值。

输出格式

包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。

样例输入

70 3
71 100
69 1
1 2

样例输出

3

数据规模和约定

对于30%的数据,M <= 10;
  对于全部的数据,M <= 100。

思路:

本题的思路跟动态规划的经典例题-01背包一致,设dp[i] [j]为采药时间为 j 的采前 i 株草药可以获得的最大价值。设value[i]为第i株草药的价值,time[i]为采第i株草药所需要的时间,则跟01背包一样,状态转移方程为:
d p [ i ] [ j ] = { d p [ i − 1 ] [ j ] , t i m e [ i ] > j d p [ i − 1 ] [ j − t i m e [ i ] ] + v a l u e [ i ] , t i m e [ i ] < j dp[i][j] = \begin{cases}dp[i - 1][j],time[i] > j\\dp[i - 1][j - time[i]] + value[i],time[i] < j \end{cases} dp[i][j]={dp[i1][j],time[i]>jdp[i1][jtime[i]]+value[i],time[i]<j
当采第i株草药所需要的时间少于要求的时间,那么dp[i] [j] 等于采第i株草药的价值,加上j - time[i]的时间里采前i - 1株草药的最大价值

代码:

#include <bits/stdc++.h>
using namespace std;
int t, m;//t表示总共能够用来采药的时间,m代表山洞里的草药的数目
int time1[105], value[105];//time[i]表示采第i株草药所需要的时间,value[i]表示第i株草药的价值
int dp[1005];//dp[j]表示给j时间所能采药得到的最大价值
int main()
{
    cin >> t >> m;
    for(int i = 1; i <= m; i++)
    {
        cin >> time1[i] >> value[i];
    }
    for(int i = 1; i <= m; i++)
    {
        for(int j = t; j >= time1[i]; j--)
        {
            dp[j] = max(dp[j], dp[j - time1[i]] + value[i]);//空间优化过,注意从t时间开始往小的循环
        }
    }
    cout << dp[t] << endl;
    return 0;
}
• 开心的金明-01背包-DP

问题描述

金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎 么布置,你说了算,只要不超过N元钱就行”。今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的N元。于是,他把每件物品规定了一 个重要度,分为5等:用整数1~5表示,第5等最重要。他还从因特网上查到了每件物品的价格(都是整数元)。他希望在不超过N元(可以等于N元)的前提 下,使每件物品的价格与重要度的乘积的总和最大。
  设第j件物品的价格为v[j],重要度为w[j],共选中了k件物品,编号依次为 j1,j2,……,jk,则所求的总和为:
  v[j1]*w[j1]+v[j2]*w[j2]+ …+v[jk]w[jk]。(其中为乘号)
  请 你帮助金明设计一个满足要求的购物单。

输入格式

输入文件 的第1行,为两个正整数,用一个空格隔开:
  N m
  (其中N(<30000)表示总钱 数,m(<25)为希望购买物品的个数。)
  从第2行到第m+1行,第j行给出了编号为j-1的物品的基本数据,每行有2个非负整数
  v p
  (其中v表示该物品的价格(v<=10000),p表示该物品的重要度(1~5))

输出格式

输出文件只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(<100000000)。

样例输入

1000 5
800 2
400 5
300 5
400 3
200 2

样例输出

3900

思路:

同样是经典的01背包问题,本题唯一的不同就是给每一个物品又加上了重要度,我们只需要先预处理,预先将每一个物品的价格与重要度乘起来,一起当作该物品的价值value即可。

状态转移方程(空间优化后):
d p [ j ] = m a x ( d p [ j ] , d p [ j − p r i c e [ i ] ] + v a l u e [ i ] ) dp[j] = max(dp[j], dp[j - price[i]] + value[i]) dp[j]=max(dp[j],dp[jprice[i]]+value[i])
其中value[i] = price[i] * imp[i],imp[i]为第i件物品的重要度

代码:

#include <bits/stdc++.h>
using namespace std;
int N, m;//N 表示买的金额不超过N元,m表示希望购买的物品数量
int price[30], imp[30], value[30];//分别表示第i件物品的价格,第i件物品的重要度,第i件物品的价值
int dp[30005];//dp[j]表示不超过j金额的物品的价格与重要度乘积(价值)的总和的最大值
int main()
{
    cin >> N >> m;
    for(int i = 1; i <= m; i++)
    {
        cin >> price[i] >> imp[i];
    }
    for(int i = 1; i <= m; i++)
    {
        value[i] = price[i] * imp[i];//预处理得到每件物品的价值
    }
    for(int i = 1; i <= m; i++)
    {
        for(int j = N; j >= price[i]; j--)
        {
            dp[j] = max(dp[j], dp[j - price[i]] + value[i]);//空间优化
        }
    }
    cout << dp[N] << endl;
    return 0;
}
• 区间k大数查询-排序-查找

问题描述

给定一个序列,每次询问序列中第l个数到第r个数中第K大的数是哪个。

输入格式

第一行包含一个数n,表示序列长度。

第二行包含n个正整数,表示给定的序列。

第三个包含一个正整数m,表示询问个数。

接下来m行,每行三个数l,r,K,表示询问序列从左往右第l个数到第r个数中,从大往小第K大的数是哪个。序列元素从1开始标号。

输出格式

总共输出m行,每行一个数,表示询问的答案。

样例输入

5
1 2 3 4 5
2
1 5 2
2 3 2

样例输出

4
2

数据规模与约定

对于30%的数据,n,m<=100;

对于100%的数据,n,m<=1000;

保证k<=(r-l+1),序列中的数<=106。

思路:

大体思路就是数组和排序查找的方法,因为需要询问多次,所以我们需要定义一个结构体数组,里面存储着每次询问的l, r, k,因为每次都是询问第l个数到第r个数,我们设另一个数组b,把数组a第l个到第r个存到b数组中,再用sort函数对b数组进行从大到小排序,最后输出第k大的数即可,注意sort传入的参数。

代码:

#include <bits/stdc++.h>
using namespace std;
int n, m;//n表示有n个正整数的序列,m表示询问个数
int a[1005];//数组a,放入给定的序列
struct node
{
    int l;//序列从左往右第l个数到第r个数中,从大往小第K大的数是哪个
    int r;
    int k;
}xunwen[1005];
int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }
    cin >> m;
    for(int i = 1; i <= m; i++)
    {
        cin >> xunwen[i].l >> xunwen[i].r >> xunwen[i].k;//将每次询问的l,r, k都读入
    }
    for(int i = 1; i <= m; i++)
    {
        int b[1005];//a数组的替代品
        int num = 0;
        for(int j = xunwen[i].l; j <= xunwen[i].r; j++)
        {
            b[num++] = a[j];//第l个数到第r个数存入b数组
        }
        sort(b, b + num, greater<int>());//从指定的一段区间里面从大到小排列数组,注意第二个参数b + num是最后一个要排序的数据的后一个数据的地址
        cout << b[xunwen[i].k - 1] << endl;//输出第k大的数
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值