Educational Codeforces Round 131 A-D题解

个人思路,仅作记录,可以参考,欢迎交流。

比赛地址:传送门


A. Grass Field

传送门

【题意】给定一个2*2的二进制矩阵,每次操作可以选定任一行和任一列并将它们清零,求最少需要多少次操作才能将矩阵清零。

【思路】情况数太少,甚至不需要分析。若矩阵全为0则答案为0,若矩阵全为1则答案为2,其余情况答案为1。

【代码】

void solve()
{
	int cnt = 0;
	int a[5];
	for (int i = 1; i <= 4; ++i)
	{
		cin >> a[i];
		if (a[i])
		{
			cnt++;
		}
	}
	if (cnt == 0)
	{
		cout << "0\n";
	}
	else if (cnt < 4)
	{
		cout << "1\n";
	}
	else if (cnt == 4)
	{
		cout << "2\n";
	}
	return;
}

B. Permutation

传送门

【题意】给定正整数n,求一个长度为n的排列p和一个正整数d使得满足p[i]*d=p[i+1]的i最多

【思路】最容易满足 p[i]*d=p[i+1] 的d显然是2,所以大胆猜测(不用求证)当d=2时一定有使得i最多的排列。要想i最多,就要将[1,n]内的数分为尽可能少的公比为2的等比序列。所以只要从1开始找以每个未出现过的数为首项、公差为2的等比序列,组合起来即为答案。

【代码】

void solve()
{
	bool vis[200005] = { 0 };
	int n;
	cin >> n;
	cout << "2\n";
	for (int i = 1; i <= n; ++i)
	{
		if (vis[i] == 0)
		{
			for (int j = i; j <= n; j *= 2)
			{
				if (j == 1)
				{
					cout << j;
				}
				else
				{
					cout << " " << j;
				}
				vis[j] = 1;
			}
		}
	}
	cout << "\n";
	return;
}

C. Schedule Management

传送门

【题意】有m个工作要分配给n个工人,已知工作i只有分配给精通i的工人a[i]才可以用1h完成,否则需要2h完成。所有工人同时开始工作且一次只能进行一个工作。求最少需要多少时间才能完成所有工作。

【思路】简单分析得知,所有工人应该尽量优先做自己精通的工作,精通的工作太多时再将工作分给闲的工人,这样才能得到最优答案,但是又难以直接确定每位工人应该做多少工作最优。所以容易想到二分查找答案并用上述思想进行验证。

【代码】

using ll = long long;

int n, m;
int a[200005];
int t[200005];

int bisearch(int lef, int rig)
{
	memset(t, 0, sizeof(t));
	if (rig - lef == 1)
	{
		return rig;
	}
	int mid = lef + (rig - lef) / 2;
	ll surp = 0;
	for (int i = 1; i <= m; ++i)
	{
		if (t[a[i]] < mid)
		{
			t[a[i]]++;
		}
		else
		{
			surp++;
		}
	}
	for (int i = 1; i <= n; ++i)
	{
		surp -= (mid - t[i]) / 2;
	}
	if (surp <= 0)
	{
		return bisearch(lef, mid);
	}
	else
	{
		return bisearch(mid, rig);
	}
}

void solve()
{
	cin >> n >> m;
	for (int i = 1; i <= m; ++i)
	{
		cin >> a[i];
	}
	cout << bisearch(0, m * 2) << "\n";
	return;
}

D. Permutation Restoration

传送门

【题意】给定长度n和数组b,求一个长度为n的排列a使得bi=⌊i/ai⌋

【思路】根据b[i]可以轻易求出a[i]的取值范围,所以问题关键在于将[1,n]的所有数按一定顺序和规则分配给每一个位置使其满足范围要求。不妨从1到n分配:考虑1,它只能分配给左边界小于等于1的范围,而根据贪心思想,在左边界小于等于1的若干个范围中,又应该选择右边界最小的范围。所以做法就是在分配每个数时从未被分配且可以分配的范围中选取又边界最小的范围。这一过程使用优先队列即可实现。

【代码】

struct INFO
{
	int rig, num;
	bool operator <(const INFO i) const
	{
		return rig > i.rig;
	}
};

void solve()
{
	int n;
	cin >> n;
	vector<int> ans(n + 1);
	int b, lef, rig;
	vector<vector<INFO>> v(n + 1);
	for (int i = 1; i <= n; ++i)
	{
		cin >> b;
		lef = i / (b + 1) + 1;
		rig = b ? i / b : n;
		v[lef].push_back({ rig,i });
	}
	priority_queue<INFO> pq;
	for (int i = 1; i <= n; ++i) //将1-n进行分配
	{
		//把v[lef]的元素放出来
		for (auto e : v[i])
		{
			pq.push(e);
		}
		ans[pq.top().num] = i;//贪心,取一个最差的
		pq.pop();
	}
	for (int i = 1; i <= n; ++i)
	{
		cout << (i == 1 ? "" : " ") << ans[i];
	}
	cout << "\n";
	return;
}

(期末一段时间没写代码生疏了,就几行代码没开long long卡了一个多小时导致掉了几千名,难受)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值