Codeforces Round #743 (Div. 2) (A~C) 题解

比赛链接:点击这里传送

官方题解链接:点击这里传送


1573A Countdown 题目链接:点击这里传送

在这里插入图片描述题意:
有一串序列,你要将他全部变成0.可以进行下述两个操作,交换两个位置的元素,是最后一位的元素减一。求完成要求的最小操作数量
思路:
有前导零的存在,先找出第一个不为0的位置。然后对接下来的数字进行模拟,分最后一位和不是最后一位以及0和非0的情况。

#include<bits/stdc++.h>
using namespace std;
string s;
int t, n;

int main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cin >> t;
	while (t--)
	{
		cin >> n>>s;
		int flag = 0;
		int ans = 0;
		int pos = 0;
		for (int i = 0; i < s.length(); i++)
		{
			if (s[i] != '0')
			{
				pos = i;
				flag = 1;
				break;
			}
		}
		if (flag == 0)
		{
			cout << 0 << endl;
			continue;
		}
		for (int i = pos; i < s.length(); i++)
		{
			int temp = s[i] - '0';
			if (i == s.length() - 1) ans += temp;
			else if(i!=s.length()-1&&temp!=0)ans += (temp + 1);
		}
		cout << ans << endl;
	}
	return 0;
}

1573B Swaps 题目链接:点击这里传送

在这里插入图片描述题意:
a数组为 [ 1 , 2 n ] \left[1,2n\right] [1,2n]中所有奇数的任意排列,b数组为 [ 1 , 2 n ] \left[1,2n\right] [1,2n]中所有偶数的任意排列。可以选择a或b数组的任意元素与其左右元素进行互换。若使a数组字典序小于b数组,最少需要几次操作。
思路:
因为奇偶性不同,所以字典序比较只需要看第一位。
对于a数组的任意元素,若要使他作为 a 1 a_1 a1,需要找出b数组中第一个大于 a i a_i ai的元素,记他的位置为j。需要的操作次数为i+j-2
这样时间复杂度为O( n 2 n^2 n2),肯定不行。
不如将a数组替换为1,3,5,7,…,2n-3,2n-1。这样如果b中的某个元素小于之前出现在递增序列的奇数,那么他也一定小于当前的奇数,也就是说只需扫一遍b数组,每次都没有必要重新从1开始扫b。
新建一个pos数组用于记录第一个大于某个奇数的偶数出现在b中的位置。最后在遍历a数组的过程中即可求解。

#include<bits/stdc++.h>
using namespace std;
#define MAXN 200005
int a[MAXN];
int b[MAXN];
int pos[2 * MAXN];
int n,t;
int main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cin >> t;
	while (t--)
	{
		cin >> n;
		int ans = 99999999;
		for (int i = 1; i <= n; i++) cin >> a[i];
		for (int i = 1; i <= n; i++) cin >> b[i];
		for (int i = 1,j=1; i < 2 * n; i += 2)
		{
			while (b[j] < i) j++;
			pos[i] = j;
		}
		for (int i = 1; i <= n; i++) ans = min(ans, i + pos[a[i]] - 2);
		cout << ans << endl;
	}
	return 0;
}

1573C Book 题目链接:点击这里传送

在这里插入图片描述
题意:
有一本书编的很烂。只有当看过某些章节后,才能看懂某一章节。定义那些章节为前置章节,必须看完所有前置章节才能看懂当前章节,没有看过或者没看完都看不懂当前章节。现在有一个人想完全看懂这本书,他会从按照顺序阅读每一章节,判断他是否能完全看懂这本书,如果可以输出一共看的轮数。
思路:
挺裸的拓扑思想。将那些没有前置章节都能看懂的章节入队,对应他们的后置章节的入度-1,如果某些后置章节的入度变为0了,也将他入队。

  • 如果后置章节j的序号大于前置章节i的序号,那么这轮还能轮到它
    t i m e s [ j ] = m a x ( t i m e s [ j ] , t i m e s [ i ] ) times\left[j\right]=max(times\left[j\right],times\left[i\right]) times[j]=max(times[j],times[i])

  • 如果后置章节j的序号小于前置章节i的序号,那么这轮不能轮到它
    t i m e s [ j ] = m a x ( t i m e s [ j ] , t i m e s [ i ] + 1 ) times\left[j\right]=max(times\left[j\right],times\left[i\right]+1) times[j]=max(times[j],times[i]+1)

    需要特别注意的是,这个过程必须在每次入度减去1时就执行而不是只在入度等于0时执行。最后一个需要看懂的前置章节的轮数不一定是最大的。
    假设看懂了某个章节x,对应能够看懂了y的前置章节a和b,恰巧a的序号小于x,b的序号大于x,遍历过程中可能a比b来的更早,b成为了你以为的最后需要看懂的前置章节,实际上a才是最后需要看懂的前置章节,程序就会产生错误。
    换句话说,入度为0是指完成了看懂这一章节的条件,并非在当前轮数就能看懂这一章节。如果还是搞不懂将代码替换一部分为这样也许能加深理解
    这是假代码:

for (auto i:v[x])
			{
				indgr[i]--;
				if (indgr[i] == 0)//因为看懂了x,能看懂y了
				{
					cnt++;				
					if (x > i) times[i] = max(times[i],times[x]+1);//得轮到下一次
					else if (x < i) times[i]=max(times[i],times[x]) ;//这一轮还能看到y
					q.push(i);
				}
			}

这是真代码:

#include<bits/stdc++.h>
using namespace std;
#define MAXN 200005
#define ll long long
int t, n;
vector <int> v[MAXN];//i的后置章节
int indgr[MAXN];
int times[MAXN];
int main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cin >> t;
	while (t--)
	{
		cin >> n;
		for (int i = 1; i <= n; i++)
		{
			v[i].clear();
			indgr[i] = 0;
			times[i] = 0;
		}
		queue <int> q;
		int cnt = 0;//看懂的书的数量
		for (int i = 1; i <= n; i++)
		{
			int m, temp;
			cin >> m;
			indgr[i] = m;
			if (m == 0)
			{
				times[i] = 1;
				q.push(i);
				cnt++;
			}
			while (m--)
			{
				cin >> temp;//先看懂temp才能看懂i
				v[temp].push_back(i);
			}
		}
		int ans = 0;//看了几轮
		while (!q.empty())
		{
			int x = q.front(); q.pop();
			//cout << "现在是" << x.val << endl;
			for (auto i:v[x])
			{
				indgr[i]--;
				if (x > i) times[i] = max(times[i], times[x] + 1);//得轮到下一次
				else if (x < i) times[i] = max(times[i], times[x]);//这一轮还能看到y
				if (indgr[i] == 0)//因为看懂了x,能看懂y了
				{
					cnt++;									
					q.push(i);
				}
			}
		}
		if (cnt != n) cout << -1 << endl;
		else
		{
			for (int i = 1; i <= n; i++) ans = max(ans, times[i]);
			cout << ans << endl;
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值