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

總結

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