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