Codeforces Round #545 (Div. 2) 解题报告

Codeforces Round #545 (Div. 2)

B. Circus

题目大意:

   n ( < = 5000 ) n(<=5000) n(<=5000)个人,有会演小丑的,有会杂耍的,有都不会以及都会的,问:将 n n n个人怎样均分成两组(每组 n / 2 n/2 n/2),使第一组可以演小丑的人数恰好等于第二组会杂耍的人?

解题思路:

  因为 n < = 5000 n<=5000 n<=5000,所以考虑的算法时间复杂度大概在 O ( n 2 ) O(n^2) O(n2)~ O ( n 2 l o g n l o g n ) O(n^2lognlogn) O(n2lognlogn)之间

  1. 将什么都不会演的统计个数为 a a a,将只会杂耍的统计个数为 b b b,将只会演小丑的统计为 c c c,将都会的统计为 d d d
  2. 枚举第一组中会演小丑的个数 t t t
  3. 因为会演小丑的只会处在 c c c d d d中,所以枚举小丑分别在 c c c d d d中选的个数 x x x以及 y y yps:x+y=t
  4. 接下来就是判断方案是否可行:
  5. 首先 x > c ∣ ∣ y > d x > c || y > d x>cy>d说明该 x x x y y y不可行, c c c d d d中没有足够的个数
  6. 其次d中剩下的个数会被分配到第二组,所以设tmpt为第二组还需要杂耍演员的个数,即为 t m p t = t − ( d − y ) tmpt = t - (d - y) tmpt=t(dy)
  7. 如果 t m p t tmpt tmpt小于0说明第二组再不选 b b b的情况下,会杂耍的都大于第一组演小丑的,说明不可行
  8. 接下来用 t m p b tmpb tmpb代表为了凑齐 t t t个人演杂耍, b b b中剩下的人数
  9. 如果 t m p b < 0 tmpb < 0 tmpb<0 说明第二组无法凑齐t人,说明不可行
  10. 如果剩余b中的人数大于 n / 2 − t n/2 - t n/2t 说明,还有一部分b仍然需要填充到第二组,使第二组中会杂耍的大于 t t t个,说明不可行
  11. 以及剩余 c c c中的人数大于 n / 2 − t n/2 - t n/2t ,说明不可行(同理与10
  12. 最后的情况即为可行情况,记录第一组中每种人数需要选的人数,循环遍历即可

AC代码:

#include<bits/stdc++.h>
using namespace std;
int n, a, b, c, d;
char jok[5010], acr[5010];
int main() {
	scanf("%d", &n);
	scanf("%s%s", jok + 1, acr + 1);
	for(int i = 1; i <= n; i++)
	    if(jok[i] == '0' && acr[i] == '0') a++;
	    else if(jok[i] == '0' && acr[i] == '1') b++;
	    else if(jok[i] == '1' && acr[i] == '0') c++;
	    else d++;
	bool vis = false;
	int t, x, y;
	int numa, numb, numc, numd;
	for(t = (d + 1) / 2; t <= n / 2; t++) {
		for(x = 0; x <= t; x++) {
			int tmpa = a, tmpb = b, tmpc = c, tmpd = d, tmpt = t;
			y = t - x;
			tmpc -= x, tmpd -= y, tmpt -= tmpd, tmpb -= tmpt;//tmpt代表第二个表演节目还需要多少个acr
			//tmpc为c剩下的
			if(tmpc < 0 || tmpd < 0 || tmpt < 0 || tmpb < 0 || tmpb > n / 2 - t || tmpc > n / 2 - t) continue;
			vis = true;
			numa = n / 2 - tmpb - x - y, numb = tmpb, numc = x, numd = y;//统计个数
			break;
		}
		if(vis) break;
	}
	//cout << numa << " " << numb << " " << numc << " " << numd << endl;
	if(!vis) {
		printf("-1\n");
		return 0;
	}
	for(int i = 1; i <= n; i++) {//遍历输出答案
		if(numa && jok[i] == '0' && acr[i] == '0') {
			printf("%d ", i);
			numa--;
		}
		else if(numb && jok[i] == '0' && acr[i] == '1') {
			printf("%d ", i);
			numb--;
		}
		else if(numc && jok[i] == '1' && acr[i] == '0') {
			printf("%d ", i);
			numc--;
		}
		else if(numd && jok[i] == '1' && acr[i] == '1') {
			printf("%d ", i);
			numd--;
		}
	}
	return 0;
}

C. Skyscrapers

题目大意:

  给出n*m的数字,然后在每个位置(i,j)上询问,将此位置的行列的数值离散化,使其在列中不改变相对大小,行中不改变相对大小,使该行列的最大值最小,并输出

解题思路:

  1. 分别按照行和列将其离散化,并存到两个不同的数组 r [ ] [ ] r[][] r[][], c [ ] [ ] c[][] c[][],以及记录行 M a x r [ ] Maxr[] Maxr[]和列 M a x c [ ] Maxc[] Maxc[]离散化后最大的值
  2. 将每个位置的 r [ i ] [ j ] r[i][j] r[i][j] c [ i ] [ j ] c[i][j] c[i][j]比较,如果 r [ i ] [ j ] > c [ i ] [ j ] r[i][j] > c[i][j] r[i][j]>c[i][j],说明该i行不需要改变其最大值,而该 j j j列,因为要变成 r [ i ] [ j ] r[i][j] r[i][j],其列最大值也改变成 M a x c [ j ] + r [ i ] [ j ] − c [ i ] [ j ] Maxc[j] + r[i][j] - c[i][j] Maxc[j]+r[i][j]c[i][j],与 M a x r [ i ] Maxr[i] Maxr[i]比较输出大的那个
  3. 其余情况同理

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> pi;
int n, m;
int a[1010][1010], r[1010][1010], c[1010][1010], Maxr[1010], Maxc[1010];
struct cmp {
	bool operator()(const pi p1, const pi p2) {
		return p1.second > p2.second;//second值小的优先 
	}
};
priority_queue <pi, vector<pi>, cmp> que;
//此处是利用优先队列离散化
int main() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++) 
	    for(int j = 1; j <= m; j++)
	        scanf("%d", &a[i][j]);
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= m; j++) que.push(pi(j, a[i][j]));
		int tmp = 0, tot = 0;
		while(!que.empty()) {
			pi x = que.top();
			que.pop();
			if(tmp != x.second) r[i][x.first] = ++tot, tmp = x.second;
			else r[i][x.first] = tot;
			Maxr[i] = tot;//记录行的最大值
		}
	}
	for(int j = 1; j <= m; j++) {
		for(int i = 1; i <= n; i++) que.push(pi(i, a[i][j]));
		int tmp = 0, tot = 0;
		while(!que.empty()) {
			pi x = que.top();
			que.pop();
			if(tmp != x.second) c[x.first][j] = ++tot, tmp = x.second;
			else c[x.first][j] = tot;
			Maxc[j] = tot;//记录列的最大值
		}
	}
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= m; j++) {
			if(r[i][j] > c[i][j]) printf("%d ", max(Maxc[j] + r[i][j] - c[i][j], Maxr[i]));
			else if(r[i][j] < c[i][j]) printf("%d ", max(Maxr[i] + c[i][j] - r[i][j], Maxc[j]));
			else printf("%d ", max(Maxr[i], Maxc[j]));
			//相等说明都不需要改变
		}
		printf("\n");
	}
}

D. Camp Schedule

题目大意:

  给定 s s s t t t字符串,将 s s s重排,使t作为子串在 s s s中出现的最多

解题思路:

  1. 利用 k m p kmp kmp中求 n e x t next next数组的方法,求出长度为 t . s i z e ( ) t.size() t.size()的最长相同前缀后缀长度
  2. 然后每次排到 t . s i z e ( ) t.size() t.size()时就跳到 n e x t [ t . s i z e ( ) ] next[t.size()] next[t.size()]

AC代码:

#include<bits/stdc++.h>
using namespace std;
int Next[500100];
string s, t;
void Get_Next() {
	int lt = t.size(), l = 0, k = -1;
	Next[0] = -1;
	while(l < lt) {
		if(k == -1 || t[k] == t[l]) {
			k++, l++;
			Next[l] = k;
		}
		else k = Next[k];
	}
}
int main() {
	ios::sync_with_stdio(false);
	cin >> s >> t;
	Get_Next();
	int num[2] = {0, 0}, pos = 0;
	for(int i = 0; i < s.size(); i++) num[s[i] - '0']++;
	while(1) {
		if(num[t[pos] - '0'] > 0) {
			cout << t[pos];
			num[t[pos] - '0']--;
			pos++;
			if(pos == t.size()) pos = Next[pos];
		}
		else break;
	}
	while(num[0]--) cout << 0;//将剩余的全部输出
	while(num[1]--) cout << 1;
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值