Codeforces Round #437B,C,D,E题目详解

B题题意:给你一个答案A,让你推出用M种硬币组成N元钱的方案数为A。
思路:构造题,其实我们很容易知道用1和2就可以构造出所有的数,然后再算一算就会发现构造出的数字的方案是有规律的。读者可以自己思考思考。

#include<iostream>
using namespace std;
int main()
{
    int A;
    cin >> A;
    cout << 2 * A - 1 << " " << 2 << endl;
    cout << 1 << " " << 2 << endl;
    return 0;
}

C题题意:有N组参赛者,他们要吃披萨,一片披萨有S块。现在有两种披萨,他们对这两种披萨的喜爱值不同,给出每组参赛者要吃多少块披萨,对两种披萨的喜爱值,要求我们在买披萨最少的前提下,保证总喜爱值最大。
思路 :对于ai大于bi的,我们最好给他吃第一种,ai小于bi的,最好给他第二种,如果相等则随意。这样算可能会把披萨算多了,对于ai>bi的组吃的披萨数记为suma,其他的披萨数记为sumb,如果suma%S+sumb%S>S,那么就上面这样算,原因读者可以思考一下。如果小于等于呢,这种情况对于多出来的部分只能买1个披萨了,所以此时要在上面的前提下分类讨论,最后一块到底是买a好还是b好。
代码如下:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
vector<pair<ll, ll>>V1, V2;
int main()
{
    ll N, S;
    cin >> N >> S;
    ll s, a, b;
    ll ans = 0;
    ll p = 0, q = 0;
    for (int i = 1;i <= N;i++)
    {
        scanf("%lld%lld%lld", &s, &a, &b);
        if (a > b)
        {
            ans += s*a;
            p += s;
            p %= S;
            V1.push_back({ a - b,s });
        }
        else
        {
            ans += s*b;
            q += s;
            q %= S;
            V2.push_back({ b - a,s });
        }
    }
    if (p + q > S)
    {
        cout << ans << endl;
        return 0;
    }
    sort(V1.begin(), V1.end());
    sort(V2.begin(), V2.end());
    ll x = 0, y = 0;
    for (auto it : V1)
    {
        x += min(it.second, p)*it.first;
        p -= min(it.second, p);
    }
    for (auto it : V2)
    {
        y += min(it.second, q)*it.first;
        q -= min(it.second, q);
    }
    cout << ans - min(x, y) << endl;
    return 0;
}

D题题意:一个人玩游戏,有n关,每一关他有2种通关时间Fi,Si,并且Fi< Si,他以Fi通过此关的概率为Pi/100,现在给你一个R,问你在保证他通过所有关时间不超过R的前提下,通关所需要的时间期望为多少?
思路:我们可以设期望为X,现在有两种操作,第一种两种方式都行,那么Xi=pi*fi+(1-pi)si,如果只能由第一种方式可行的话,那么X=pi*fi+(1-pi)(si+X)。读者可以仔细思考一下这个公式。
但现在的情况有些变化,前面的选择会影响后面的选择。所以我们可以设dp[i][j]为已经到第i关,前面i-1关花了j时间。
那么dp[i][j]=pi*(dp[i+1][j+f[i]])+(1-pi)(dp[i+1][j+s[i]])(两个操作都行时)。dp[i][j] = pi(dp[i+1][j+f[i]])+(1-pi)*(X+j+s[i])(第二种操作不行,所以要重新返回第一关)。还有两种操作都不行的dp方程,与上面类似。
那么dp[1][0]其实就等于X,其实就是来解这个一元一次方程。那么这里我们可采用二分的方式求X。
代码如下:

#include<iostream>
#include<algorithm>
using namespace std;
int F[55], S[55];
double  P[55];
double dp[55][5005];
const double inf = 0x3f3f3f3f;
int main()
{
    int N, R;
    double f, s, p;
    cin >> N >> R;
    for (int i = 1;i <=N;i++)
        cin >> F[i] >> S[i] >> p, P[i] = p / 100;
    double l = 0, r = 1e15;
    double mid;
    for (int k = 1;k < 100;k++)
    {
        mid = (l + r) / 2;
        for (int t = 1;t <= R;t++)
            dp[N + 1][t] = t;
        for (int t = R + 1;t < 5005;t++)
            dp[N + 1][t] = inf;
        for (int i = N;i >= 1;i--)
            for (int t = 0;t < 5005;t++)
                dp[i][t] = min(mid + (double)(t + F[i]), dp[i + 1][t + F[i]])*P[i] + min(mid + (double)(t + S[i]), dp[i + 1][t + S[i]])*(1 - P[i]);
        if (dp[1][0] > mid)
            l = mid;
        else
            r = mid;
    }
    printf("%.9f\n", mid);
}

这是一个好题啊,博主想了很久才弄清楚,希望读者也要搞清楚啊。
E题题意:买卖股票,有N天股票的价格,问你怎么弄在N天后可以获得的钱数最多(可以欠账)。
思路:对于每天的股票我们其实有三种操作,买,卖,不管。那么我们只要想办法将这三种操作分开就行了,并且要保证买卖的次数相同。这里用到multiset,对于在集合里面的数,如果出现2次,就代表它被卖掉,1次则是不管,0次则代表他被买了。每次新增一个数,先往集合里面添加2次,然后再删掉最小的一个数。这里的含义就是对于这个数来说,之前如果集合中最小的比它小,不管是有两个还是一个(代表卖掉它还是不管它),都减一个(如果之前是卖掉它,那么现在明显是不管它并且卖掉当前这个比较划算,如果之前是不管它,那么现在最好就是买它并且卖掉当前这个划算)。
其中的逻辑读者可以再仔细推敲。
代码如下:

#include<iostream>
#include<set>
using namespace std;
typedef long long ll;
multiset<ll>S;
int main()
{
    int n;
    cin >> n;
    ll ans = 0;
    ll temp;
    for (int i = 1;i <= n;i++)
    {
        scanf("%lld", &temp);
        S.insert(temp);
        S.insert(temp);
        S.erase(S.begin());
        ans -= temp;
    }
    for (auto it : S)
    {
        ans += it;
    }
    cout << ans << endl;
}

这场虽然代码简短,但是题目难度还是很大。博主从中认识到了自己和大佬的差距,补完题目之后没有了之前那么愉悦感,因为代码太简短了。。但为什么自己就做不出来。希望自己今后要更加努力才对啊。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值