Codeforces Round #648 (Div. 2)A~E题解

传送门

A.Matrix Game

      给你一个 n × m n×m n×m的01矩阵,两个玩家一先一后把为0的 a a a i j ij ij变为1,条件为第 i i i行和第 j j j列没有1,当一名玩家无点可改,lose。我们每改一个点,就会有1行和1列失效。显而易见,在这个 n × m n×m n×m的01矩阵中,最多可以变 m i n ( n , m ) min(n, m) min(n,m)个点。
       因为在最初给的01矩阵中,也可能有1,我们要先算出还有几个点可改,再判奇偶性即可

#define clr(a, b) memset((a), (b), sizeof(a))
int n, m, a;
int l[55], r[55];
void solve()
{
	clr(l, 0);//有t个case,记得初始化
	clr(r, 0);
	cin >> n >> m;
	int L = n, R = m;//记录还有几行几列是没有1的
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
		{
			cin >> a;
			if (a == 1)
			{
				if (l[i] == 0)L--;
				if (r[j] == 0)R--;
				l[i] = r[j] = 1;
			}
		}
	if (min(L, R) % 2 == 1)cout << "Ashish\n";
	else cout << "Vivek\n";

}

B.Trouble Sort

       给你一个包含 n n n个数的数组 a [ ] a[] a[],和一 一对应的type数组 b [ ] b[] b[](只有0,1)。你可以交换两个数 a a a i i i, a a a j j j当且仅当 b b b i i i不等于 b b b j j j。问能否经过任意次交换,使得数组 a a a是非递减的。
       首先我们知道,如果type全为0或1,这个数组是操作不了的,这时候判断他最初是不是非递减的,如果不是,就是NO。当type数组中有至少1个不同时,他是可以任意调换的:我们假设要交换相同type的 a a a i i i a a a j j j,我们找到与他们type不同的 a a a k k k,然后以 a a a k k k为中介, s w a p ( swap( swap( a a a i i i, a a a k k k ) ) ), s w a p ( swap( swap( a a a j j j, a a a k k k ) ) ), s w a p ( swap( swap( a a a i i i, a a a k k k ) ) ),这样就交换成功了。因此只要存在至少1个type是与其他不同的,这个数组就可以任意调换(可以自己举个例子,就清楚了),要变成非递减的也不是问题。

int n, m, x, y;
int a[550], b[550];
void solve()
{
	int z = 0, o = 0;
	cin >> n;
	for (int i = 1; i <= n; i++)cin >> a[i];
	for (int i = 1; i <= n; i++)
	{
		cin >> b[i];
		if (b[i] == 1)o = 1;
		if (b[i] == 0)z = 1;
	}
	bool f = 0;
	for (int i = 1; i < n; i++)
		if (a[i] > a[i + 1])f = 1;
	if (f && (z + o != 2))cout << "NO\n";//当type只有0或只有1,且原数组不是非递减的,输出NO
	else cout << "YES\n";
}

C.Rotation Matching

给你包含 n n n个数的 a a a, b b b数组,他们都是 [ 1 , n ] [1,n] [1,n]的任意一个排列,你可以左移或右移任意数组任意位数一次,问在所有情况中, a a a, b b b数组能对上的位置最多有多少个。当 i i i = = == == j j j a a a i i i = = == == b b b i i i,我们说这个位置能对上。左移一个数组表示在下方,右移相反。在这里插入图片描述
这道题,可能很多人最开始会想到kmp,hash,或者最长公共子序列,是的这些我都想过。 在真正了解了题意后,我们发现,找到某个数字 x x x a a a b b b数组中的位置,他们的下标差( p o s a [ x ] − p o s b [ x ] posa[x]-posb[x] posa[x]posb[x])即为:要使这个数字对上,需要左移或右移数组 b b b(这里我们固定 a a a数组,只移动 b b b数组,当然反着来也行)几个位置。在这里我们把右移当作正方向(下标差为正),左移当作负方向(下标差为负),如果下标差为2,表示右移数组 b b b,数字 x x x能对上,也表示当移动2位时,能对上的数字个数加1。我们不妨用num数组记录移动位置数不同时,能对上的数字个数。由于数组没有负的下标,且左移 x x x位等价于右移 n − x n-x nx位,所以把左移 x x x位的 n u m [ − x ] num[-x] num[x]表示为 n u m [ n − x ] num[n-x] num[nx]。可能我的表达不够清晰,看下面的简介代码就知道了。

int n, m;
int a[200005], b[200005];
int posa[200005], posb[200005];//记录每个数字的位置
int num[200005];
void solve()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		posa[a[i]] = i;
	}
	for (int i = 1; i <= n; i++)
	{
		cin >> b[i];
		posb[b[i]] = i;
	}
	for (int i = 1; i <= n; i++)
	{
		int dif = posa[i] - posb[i];
		if (dif < 0)dif += n;//小于0表示这里要左移,下标+n,表示为右移
		num[dif]++;
	}
	//num[0]表示不移动时能对上的数字个数,移动n次就是没有移动,可以不用加入比较
	cout << *max_element(num, num + n) << endl;//找到num数组[0,n)中最大的数
}

D.Solve The Maze

给你 n × m n×m n×m的迷宫,包含如下几种cell。
      Empty — ‘.’
      Wall — ‘#’
      Good person — ‘G’
      Bad person — ‘B’
保证出口的cell(n, m)为Empty。
我们可以在任意Empty的cell上建立一个Wall,使得这个cell无法到达。B和G只能上下左右移动,且不能移动到Wall的cell,但可以移动到B或G的cell。问我们能不能通过建墙,使得所有B不能到达出口,且所有G都能到达出口。先把B困住是比较容易的,只要把B的4个方向都建墙就行。建好后再从出口bfs一遍,把能走到的cell标记一下,看看有没有哪个G的位置是没有被标记到的。有很多细节,看代码。

#define pii pair<int, int> 
#define pb push_back
#define clr(a, b) memset((a), (b), sizeof(a))
int n, m, x, y;
char mp[55][55];
vector<pii>G, B;//存放BG的坐标
bool vis[55][55];//标记bfs过后能走到的位置
int dir[4][2] = { {1, 0},{-1, 0},{0,1},{0,-1} };//bfs四个方向
void bfs(int x, int y)//最简单的bfs模板
{
	queue<pii>q;
	q.push(pii(x, y));
	vis[x][y] = 1;
	while (q.size())
	{
		pii tmp = q.front();
		q.pop();
		for (int i = 0; i < 4; i++)
		{
			int nx = tmp.first + dir[i][0];
			int ny = tmp.second + dir[i][1];
			if (nx > 0 && nx <= n && ny>0 && ny <= m && !vis[nx][ny] && mp[nx][ny] != '#')
			{
				vis[nx][ny] = 1;
				q.push(pii(nx, ny));
			}
		}
	}
}
void solve()
{
	G.clear();
	B.clear();
	clr(mp, ' ');
	clr(vis, 0);//mp也要初始化,我们在给B周围建墙的时候,如果B是在mp边界的,会被前面没有清空的mp影响
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
		{
			cin >> mp[i][j];
			if (mp[i][j] == 'B')B.pb(pii(i, j));
			else if (mp[i][j] == 'G')G.pb(pii(i, j));
		}
	bool f = 0;
	//B的位置也要用数组存起来是因为,如果你是去遍历mp,找到B然后建墙,就会把B周围的B用Wall覆盖,明显是错的
	for (pii p : B)
	{
		int i = p.first, j = p.second;
		//如果B旁边就是G,那么只要存在G,且G能到出口,B也能,所有答案一定是NO
		if (mp[i - 1][j] == 'G' || mp[i + 1][j] == 'G' || mp[i][j + 1] == 'G' || mp[i][j - 1] == 'G')
		{
			f = 1;
			break;
		}
		if (mp[i - 1][j] == '.')mp[i - 1][j] = '#';
		if (mp[i + 1][j] == '.')mp[i + 1][j] = '#';
		if (mp[i][j + 1] == '.')mp[i][j + 1] = '#';
		if (mp[i][j - 1] == '.')mp[i][j - 1] = '#';//只有Empty的cell才可以建墙
	}
	//我们在给B建墙的时候,可能会在出口处也建了墙,那么如果迷宫里面有G,是肯定出不来的
	if (f || (G.size() > 0 && mp[n][m] == '#'))
	{
		cout << "NO\n";
		return;
	}
	if (mp[n][m] != '#')bfs(n, m);//如果出口没有被堵住,就bfs
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
		{
		//B在被标记过的位置,表示可以逃出去
			if (vis[i][j] && mp[i][j] == 'B')
			{
				cout << "NO\n";
				return;
			}
		}
	for (pii p : G)
	{
	//只要有一个G不在被标记的位置,答案为NO
		if (vis[p.first][p.second] != 1)
		{
			cout << "NO\n";
			return;
		}
	}
	cout << "YES\n";
}

E.Maximum Subsequence Value

题意是,给你 n n n个数,要你选出 k k k个数,先把他们转为2进制,对于二进制的第 i i i位,如果你选的 k k k个数里至少有 m a x ( 1 , k − 2 ) max(1, k-2) max(1,k2)个数字的二进制的第 i i i位是1,答案就+= 2 i 2^i 2i。求怎么取 k k k个数能让最后答案为 Σ 2 i \Sigma2^i Σ2i最大。比如3,4,1,二进制为011,100, 001。三个数字全选,二进制里三个位置的1的数量都是至少有 m a x ( 1 , 3 − 2 ) max(1, 3-2) max(1,32)个,因此答案为 2 0 + 2 1 + 2 2 = 7 2^0+2^1+2^2=7 20+21+22=7
我们观察 m a x ( 1 , k − 2 ) max(1, k-2) max(1,k2),发现当 k = 0 , 1 , 2 , 3 k=0,1,2,3 k=0,1,2,3时结果都是1。所以我们可以至少选3个数(贪心)。现在我们来比较一下 k k k取3和 k k k取4,对于答案有什么影响。当k=3,我们选三个数字,假设有x个位置的1的数量是符合要求的(数字1的数量要求为1)。现在我们取k=4,即在这3个数基础上再选一个数,这时候数字1的数量要求就是2了,那么你选的前3个数的x个为1的位置,第4个数对应的位置都要为1,且第4个数还要有除了这x个位置外的位置要为1,答案才能增加,否则,选的越多,答案可能就会越小。显而易见,当我们取了最优的3个数之后,再取一个数,都可能会使答案变小。
比如1,3,8,12,我们取最优的三个数:1,3,12,这时候有4个位置符合要求,如果现在你还要选8,符合要求的位置数就会降为1。
我们就可以暴力n^3取最优的三个数,就可以得到答案。
1的数量越多,或者1的位置越靠前都不一定是最优的,可以很容易举出反例。

int n;
ll a[505];
ll ans = 0;
void solve()
{
	cin >> n;
	for (int i = 0; i < n; i++)
		cin >> a[i];
	for (int i = 0; i < n; ++i) {
		for (int j = 0; j < n; ++j) {
			for (int k = 0; k < n; ++k) {
			//   |这个竖杠表示当两个都为0是,结果才为0
				ans = max(ans, a[i] | a[j] | a[k]);
			}
		}
	}
	cout << ans << endl;
}

感觉对cf的比赛有点上瘾了,难道这就是人菜瘾大吗QAQ…

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值