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

来源:互联网 发布:图片纠偏软件 编辑:程序博客网 时间:2024/06/02 04:10

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;}

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

原创粉丝点击