Codeforces Round #552 (Div. 3) 题解

题目链接

A. Restoring Three Numbers

#include 
#include 
#define fst first
#define sed second
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;

int main()
{
#ifdef LOCAL
	//freopen("C:/input.txt", "r", stdin);
#endif
	ll x, y, z, p;
	cin >> x >> y >> z >> p;
	if (p < x)
		swap(x, p);
	if (p < y)
		swap(y, p);
	if (p < z)
		swap(z, p);
	printf("%I64d %I64d %I64d\n", p - x, p - y, p - z);

	return 0;
}

B. Make Them Equal

可以将某个数字加一次v或者减一次v不限制操作次数 找到一个最小的v可以将数字变为相同

#include 
#include 
#define fst first
#define sed second
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;

int main()
{
#ifdef LOCAL
	//freopen("C:/input.txt", "r", stdin);
#endif
	vector<int> v;
	int n;
	cin >> n;
	for (int i = 0; i < n; ++i)
	{
		int x;
		cin >> x;
		v.push_back(x);
	}
	sort(v.begin(), v.end());
	v.erase(unique(v.begin(), v.end()), v.end());
	if (v.size() > 3)
		cout << -1 << endl, exit(0);
	if (v.size() == 3 && v[1] - v[0] != v[2] - v[1])
		cout << -1 << endl, exit(0);
	if (v.size() == 3)
		cout << v[1] - v[0] << endl;
	else if (v.size() == 2)
	{
		int x = v[1] - v[0];
		if (x % 2 == 0)
			x /= 2;
		cout << x << endl;
	}
	else
		cout << 0 << endl;

	return 0;
}

C. Gourmet Cat <枚举>

三种食物 告诉你周几吃哪种食物 问从周几出发能够吃的最长时间

#include 
#include 
#define fst first
#define sed second
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;

int main()
{
#ifdef LOCAL
	//freopen("C:/input.txt", "r", stdin);
#endif
	ll x[3], z[3];
	cin >> x[0] >> x[1] >> x[2]; //147 26 35
	ll X = min({ x[0] / 3, x[1] / 2, x[2] / 2 }); //整周
	x[0] -= X * 3, x[1] -= X * 2, x[2] -= X * 2; //余下物品
	ll ans = X * 7;
	int y[] = { 0, 1, 2, 0, 2, 1, 0 };
	for (int i = 0; i < 7; ++i) //开始
	{
		z[0] = x[0], z[1] = x[1], z[2] = x[2];
		int cnt = 0;
		for (int j = i; z[y[j]]--; j = (j + 1) % 7)
			cnt++;
		ans = max(ans, X * 7 + cnt);
	}
	cout << ans << endl;

	return 0;
}

D. Walking Robot <模拟>

机器人有普通电池和太阳能充电电池 0为无光1为有光 可以选择消耗一个电池走一步 阳光下可以消耗一个普通电池走一步获得一个充电电池 问最远能走多远

#include 
#include 
#define fst first
#define sed second
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;

int main()
{
#ifdef LOCAL
	//freopen("C:/input.txt", "r", stdin);
#endif
	int n, a, b; //充电电池 电池
	cin >> n >> b >> a;
	int A = a, B = b;
	for (int i = 1; i <= n; ++i)
	{
		int s;
		scanf("%d", &s);
		if (s && B && A < a)
			A = min(a, A + 1), --B;
		else if (A)
			--A;
		else
			--B;
		if (!A && !B)
			cout << i << endl, exit(0);
	}
	cout << n << endl;

	return 0;
}

E. Two Teams <数据结构> <模拟>

两个教练选人 每次选数值最大的并且将旁边的k个也选走 问每个人都被那个教练选走了

#include 
#include 
#define fst first
#define sed second
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 10;
int l[N], r[N]; //左侧右侧编号
int a[N], b[N], c[N]; //b表示a数组从大到小编号

void sel(int x, int p) //选中x
{
	r[l[x]] = r[x], l[r[x]] = l[x]; //拼接lr
	c[x] = p; //记录结果
}
int main()
{
#ifdef LOCAL
	//freopen("C:/input.txt", "r", stdin);
#endif
	int n, m;
	cin >> n >> m;
	for (int i = 1; i <= n; ++i)
		scanf("%d", &a[i]), l[i] = i - 1, r[i] = i + 1, b[i] = i;
	sort(b + 1, b + n + 1, [](int x, int y) { return a[x] > a[y]; });
	int p = 1;
	for (int i = 1; i <= n; ++i) //从大到小选取
	{
		if (c[b[i]]) //已经被选
			continue;
		int k = l[b[i]]; //两侧坐标
		for (int j = 0; k && j < m; ++j)
			sel(k, p), k = l[k];
		k = r[b[i]];
		for (int j = 0; k && j < m; ++j)
			sel(k, p), k = r[k];
		sel(b[i], p);
		p = p == 1 ? 2 : 1;
	}
	for (int i = 1; i <= n; ++i)
		putchar('0' + c[i]);
	cout << endl;

	return 0;
}

写丑了的map版

#include 
#include 
#define fst first
#define sed second
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f; 
const int N = 2e5 + 10;
bool one[N]; //是否1队

int main()
{
#ifdef LOCAL
	freopen("C:/input.txt", "r", stdin);
#endif
	map<int, int> pos, val; //位置顺序 值顺序
	int n, k;
	cin >> n >> k;
	for (int i = 1; i <= n; ++i)
	{
		int v;
		scanf("%d", &v);
		pos[i] = v;
		val[v] = i;
	}
	int flag = 1;
	while (!val.empty())
	{
		int p = val.rbegin()->second; //最大值位置
		for (int i = 0; i < k; ++i) //左侧
		{
			auto it = pos.lower_bound(p);
			if (it == pos.begin())
				break;
			--it;
			if (flag) one[it->first] = 1;
			val.erase(it->second);
			pos.erase(it);
		}
		for (int i = 0; i < k; ++i) //右侧
		{
			auto it = pos.upper_bound(p);
			if (it == pos.end())
				break;
			if (flag) one[it->first] = 1;
			val.erase(it->second);
			pos.erase(it);
		}
		if (flag) one[p] = 1;
		val.erase(pos[p]);
		pos.erase(p);
		flag ^= 1;
	}
	for (int i = 1; i <= n; ++i)
		putchar(one[i] ? '1' : '2');
	cout << endl;

	return 0;
}

F. Shovels Shop <贪心>

n个物品买k个 有m个优惠每买x个物品其中y个就免费 问最小费用
如果买k个不管有没有优惠肯定买最便宜的k个,每个优惠策略可以重复使用所以如果同样是买x个一个优惠y1一个优惠y2则取最大的。
购买使用优惠时肯定是将大小相近的作为一组一起购买,因为尽量优惠较大的。进行排序(其实找到前k小已经排序了)。
令f[i]表示购买前i个的最小代价,枚举已经购买的物品数量尝试使用每个优惠进行转移取最小值。

#include 
#include 
#define fst first
#define sed second
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 10;
int a[N], b[N], s[N];
int f[N]; //买前i个物品的最小代价

int main()
{
#ifdef LOCAL
	//freopen("C:/input.txt", "r", stdin);
#endif
	int n, m, k;
	cin >> n >> m >> k;
	for (int i = 1; i <= n; ++i)
		scanf("%d", &a[i]);
	sort(a + 1, a + n + 1); //贪心选择尽量便宜的k个 并且dp过程中需要价值连续
	for (int i = 1; i <= k; ++i)
		s[i] = s[i - 1] + a[i]; //前缀和优化转移
	for (int i = 0; i < m; ++i)
	{
		int x, y; //买x免费y
		scanf("%d%d", &x, &y);
		b[x] = max(b[x], y); //尽量多
	}
	memset(f, 0x3f, sizeof(f));
	f[0] = 0; //没有物品代价0
	for (int i = 1; i <= k; ++i) //物品
		for (int j = 1; j <= i; ++j) //一次性购买量
			f[i] = min(f[i], f[i - j] + s[i] - s[i - j + b[j]]); //只花费后半部分
	cout << f[k] << endl;

	return 0;
}

G. Minimum Possible LCM <枚举>

任选两个数字 求最小的最小公倍数
记录每个数字出现次数和出现下标,暴力枚举公因子i并乘上倍数j查询数值是否出现,如果同一个公因子i有两个倍数j出现则计算答案并且break,因为后面会更大。
即使是极端情况复杂度也在1e8以内。

#include 
#include 
#define fst first
#define sed second
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e7 + 10;
int cnt[N], pos[N]; //数值出现次数 数值编号

int main()
{
#ifdef LOCAL
	//freopen("C:/input.txt", "r", stdin);
#endif
	int n;
	cin >> n;
	ll ans = LINF, p, q;
	for (int i = 1; i <= n; ++i)
	{
		int x;
		scanf("%d", &x);
		++cnt[x];
		if (cnt[x] > 1 && ans > x) //出现2次可以直接作为答案
			ans = x, p = pos[x], q = i; //记录上次的编号和i
		pos[x] = i;
	}
	for (int i = 1; i < N; ++i) //公约数
	{
		int k = 0; //当前公约数最小的数字的倍率
		for (int j = 1; i * j < N; ++j) //倍率
			if (cnt[i * j])
			{
				if (k) //之前已有一个
				{
					if (ans > 1LL * i * j * k) //公约数*两个数的倍
						ans = 1LL * i * j * k, p = pos[i * k], q = pos[i * j]; //记录编号
					break; //后面只会更大
				}
				else
					k = j; //记录倍率
			}
	}
	if (p > q)
		swap(p, q);
	cout << p << " " << q << endl;

	return 0;
}

你可能感兴趣的:(_动态规划_,_数据结构_,Codeforces,贪心,暴力枚举,模拟)