Codeforces Round #622 (Div. 2)
前言
- 第一次打cf,有点惨。签到题签了一个小时,还贡献了4次wa,搞得后面的题没时间写,最后惨掉分。。。。
比赛AC
A.Fast Food Restaurant
简明题意
- 厨师有3种原料a,b,c各若干份,现在厨师要把这些原料组合起来做成菜。
- 现在有一些规则:1.同一种原料在同一道菜中只能使用一次。2.每一道菜至少有一种原料。3.每道菜的原料不能完全相同
- 问在满足上述规则的情况下,最多能做多少道菜。
正文
- 我当时一点想法都没有,最后abc排序,暴力写的。第一次因为忘了去掉读文件wa了,后来3次都是有个if写错了。。。。暴力有多暴力可以看看下面的代码。。。。。
- 讲讲正常点的做法,按照上面的暴力是枚举原料,但注意到菜最多7种,我们直接枚举菜就可以了。
- 假设3种原料的数量分别是abc,首先一种原料做一道菜肯定是最优解,也就是直接判断abc是否>=1,如果是,那么答案++,原料的数量- -。
- 接下来考虑两种原料做一道菜,直接枚举ab,ac,bc可以吗?看这种情况,223,首先一种原料做一道菜,ans=3,abc:112,那么先做ab,答案就是4,然后没有别的菜可以做了。但是如果先做ac,那么就还有bc可以做,答案就是5。所以解决方案是枚举两道菜的时候,优先用数量最多的原料做,也就是ab,ac,保证其中的a是最大的就可以了。
- 最后判断一下abc是否同时有,有的话答案++就可以了。
代码
暴力(太笨了)
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
void solve()
{
int t;
cin >> t;
while (t--)
{
int a, b, c;
cin >> a >> b >> c;
if (a > 4) a = 4;
if (b > 4) b = 4;
if (c > 4) c = 4;
if (a > b) swap(a, b);
if (c < a) swap(a, c), swap(b, c);
else if (c < b) swap(b, c);
int ans = 0;
if (c == 0) ans = 0;
if (c == 1)
{
if (a == 0 && b == 0) ans = 1;
if (a == 0 && b == 1) ans = 2;
if (a == 1 && b == 1) ans = 3;
}
else if (c == 2)
{
if (a == 0)
{
if (b == 1) ans = 2;
if (b == 0) ans = 1;
if (b == 2) ans = 3;
}
else if (a == 1)
{
if (b == 1) ans = 3;
if (b == 2) ans = 4;
}
else if (a == 2)
ans = 4;
}
else if (c == 3)
{
if (a == 0)
{
if (b == 0) ans = 1;
if (b == 1) ans = 2;
if (b == 2 || b == 3) ans = 3;
}
else if (a == 1)
{
if (b == 1) ans = 3;
if (b == 2) ans = 4;
if (b == 3) ans = 4;
}
else if (a == 2)
{
if (b == 2) ans = 5;
if (b == 3) ans = 5;
}
else if (a == 3)
{
ans = 6;
}
}
else if (c == 4)
{
if (a == 0)
{
if (b == 0) ans = 1;
if (b == 1) ans = 2;
if (b >= 2) ans = 3;
}
else if (a == 1)
{
if (b == 1) ans = 3;
else ans = 4;
}
else if (a == 2)
{
ans = 5;
}
else if (a == 3)
{
ans = 6;
}
else if (a == 4)
{
ans = 7;
}
}
cout << ans << endl;
}
}
int main()
{
//freopen("Testin.txt", "r", stdin);
solve();
return 0;
}
正常点的做法
nclude<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
void solve()
{
int t;
cin >> t;
while (t--)
{
int a, b, c;
cin >> a >> b >> c;
//保证c最大
if (a > c) swap(a, c);
if (b > c) swap(b, c);
int ans = 0;
if (a) a--, ans++;
if (b) b--, ans++;
if (c) c--, ans++;
if (c && b) c--, b--, ans++;
if (c && a) a--, c--, ans++;
if (a && b) a--, b--, ans++;
if (a && b && c) a--, b--, c--, ans++;
cout << ans << endl;
}
}
int main()
{
//freopen("Testin.txt", "r", stdin);
solve();
return 0;
}
C1. Skyscrapers (easy version)
简明题意
- 有1-n共n块土地,要在每块土地上建一栋楼,现在给出每块土地可以建楼的最大高度a[i]
- 现在你来给1-n每块土地建楼,要满足两个要求:1.土地i的楼高度<=a[i]。2.每栋楼的左边和右边不能存在同时比它高的楼
- 问满足上述要求的情况下,求出总楼层高度最高的建造方案
正文
- 这个几分钟我就A了,需要一个单峰的。直接暴力枚举每栋楼作为最高楼的总高度,拿到最优解就可以了。
- 这里我忘了开long long,wa了一次。
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
long long a[1100], x[1100];
void solve()
{
long long n;
cin >> n;
for (long long i = 1; i <= n; i++)
cin >> a[i];
long long ans = 0, xx[1100];
for (long long i = 1; i <= n; i++) // i作为最高塔
{
long long cur = a[i], las = a[i];
for (long long j = i - 1; j >= 1; j--)
{
las = min(las, a[j]);
cur += las;
x[j] = las;
}
las = a[i];
x[i] = a[i];
for (long long j = i + 1; j <= n; j++)
{
las = min(las, a[j]);
cur += las;
x[j] = las;
}
if (cur > ans)
{
ans = cur;
for (long long i = 1; i <= n; i++)
xx[i] = x[i];
}
}
for (long long i = 1; i <= n; i++)
{
cout << xx[i];
if (i != n) cout << " ";
}
}
int main()
{
//freopen("Testin.txt", "r", stdin);
solve();
return 0;
}
赛后补题
C2. Skyscrapers (hard version)
简明题意
- 同c1,数据范围扩大到5e5
正文
- 和c1一样的思路。c1枚举了以每个i作为最高塔时,左右两边的贡献。左右两边的贡献怎么算的?枚举。这一题中,可以用单调栈简化左右两边的贡献的计算。具体如下
- 假设先计算左边的贡献吧。设pre[]为“第i个元素的左边第一个比a[i]小的下标”。设s_pre[]为“以第i个元素为最高,左端的所有贡献”。那么s_pre[i]=s_pre[pre[i]]+(i-pre[i])*a[i]。这样,反着再来一趟,然后再去枚举1-n作为最高点就可以了。
- 注意一点点细节,思考一下当一个元素i的左边没有比它小的元素,那么pre[i]应该设为多少呢?为0,换成右边,是n+1.
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<stack>
#include<string>
using namespace std;
const int maxn = 500000 + 10;
int a[maxn];
stack<pair<int,int>> s;
int pre[maxn], beh[maxn];
long long s_pre[maxn], s_beh[maxn];
long long ans[maxn];
void solve()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
for (int i = 1; i <= n; i++)
{
while (!s.empty() && a[i] < s.top().first)
s.pop();
if (i != 1) pre[i] = s.empty() ? 0 : s.top().second;
s_pre[i] = s_pre[pre[i]] + 1ll * (i - pre[i] ) * a[i];
s.push(pair<int, int>(a[i], i));
}
while (s.size()) s.pop();
beh[n] = n + 1;
for (int i = n; i >= 1; i--)
{
while (!s.empty() && a[i] < s.top().first)
s.pop();
if (i != n) beh[i] = s.empty() ? n+1:s.top().second;
s_beh[i] = s_beh[beh[i]] + 1ll * (beh[i] - i) * a[i];
s.push(pair<int, int>(a[i], i));
}
long long max_val = -1, max_index;
for (int i = 1; i <= n; i++)
if (s_pre[i] + s_beh[i] - a[i] > max_val)
max_val = s_pre[i] + s_beh[i] - a[i], max_index = i;
ans[max_index] = a[max_index];
for (int i = max_index - 1; i >= 1; i--)
ans[i] = min((long long)a[i], ans[i + 1]);
for (int i = max_index + 1; i <= n; i++)
ans[i] = min((long long)a[i], ans[i - 1]);
for (int i = 1; i <= n; i++)
cout << ans[i] << " ";
}
int main()
{
//freopen("Testin.txt", "r", stdin);
solve();
return 0;
}
B. Different Rules
简明题意
- 一场比赛分两回合。现在有n个人参加这个比赛。每个人第一回合会获得一个名次x,第二回合会获得一个名次y。整个比赛的最终的排名,是x+y的值,越小,排名越靠前。那么假设现在知道一个选手的x和y值,问他的可能的最好名次和最差名次。
正文
1 | 1 |
2 | 2 |
3 | 3 |
x | y |
… | … |
n | n |
- 想要获得最差名次,需要尽可能大。最好名次,就是尽可能小。
- 先说最差名次。第一回合的第1名要和第二回合的x+y-1名组合,第2名要和x+y-2组合,第x+y-1名要和第1名组合。这样以来,刚好共有x+y-1人满足条件,答案就是x+y-1.但这个的前提是x+y-1<=n,否则,答案就是n。所以
- 再说最好名次。需要尽可能多的人,。显然,第一回合的第1名,应该和第二回合的x+y名组合。这时
1. 如果x+y<=n,那么接着,第二名和x+y-1名组合,最终答案就是n。
2. 如果x+y>n,那么第一名就不再和n组合,而是和第一名给组合。第二名和第二名组合,直到,第k名和第n名组合,满足k+n>x+y,这个k=x+y-n,那么有k个人,排名会<=x+y,包括自己,所以最好的名次就是k+1,也就是x+y-n+1
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<stack>
#include<string>
using namespace std;
const int maxn = 500000 + 10;
void solve()
{
int t;
cin >> t;
while (t--)
{
int n, x, y;
cin >> n >> x >> y;
cout << min(max(x + y + 1 - n, 1),n) << " " << min(x + y - 1, n) << endl;
}
}
int main()
{
//freopen("Testin.txt", "r", stdin);
solve();
return 0;
}