Codeforces Round #622 (ABC1C2)


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值,问他的可能的最好名次和最差名次。

正文

x0x_0 y0y_0
1 1
2 2
3 3
x y
n n
  • 想要获得最差名次,需要(x0+y0<=x+y)\sum (x_0+y_0<=x+y)尽可能大。最好名次,就是(x0+y0<=x+y)\sum (x_0+y_0<=x+y)尽可能小。
  • 先说最差名次。第一回合的第1名要和第二回合的x+y-1名组合,第2名要和x+y-2组合,第x+y-1名要和第1名组合。这样以来,刚好共有x+y-1人满足条件,答案就是x+y-1.但这个的前提是x+y-1<=n,否则,答案就是n。所以min(x+y1,n)min(x+y-1,n)
  • 再说最好名次。需要尽可能多的人,x0+y0>x+yx_0+y_0>x+y。显然,第一回合的第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;
}

总结

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章