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)之间
- 将什么都不会演的统计个数为 a a a,将只会杂耍的统计个数为 b b b,将只会演小丑的统计为 c c c,将都会的统计为 d d d
- 枚举第一组中会演小丑的个数 t t t
- 因为会演小丑的只会处在 c c c与 d d d中,所以枚举小丑分别在 c c c, d d d中选的个数 x x x以及 y y y,ps:x+y=t
- 接下来就是判断方案是否可行:
- 首先 x > c ∣ ∣ y > d x > c || y > d x>c∣∣y>d说明该 x x x, y y y不可行, c c c, d d d中没有足够的个数
- 其次d中剩下的个数会被分配到第二组,所以设tmpt为第二组还需要杂耍演员的个数,即为 t m p t = t − ( d − y ) tmpt = t - (d - y) tmpt=t−(d−y)
- 如果 t m p t tmpt tmpt小于0说明第二组再不选 b b b的情况下,会杂耍的都大于第一组演小丑的,说明不可行
- 接下来用 t m p b tmpb tmpb代表为了凑齐 t t t个人演杂耍, b b b中剩下的人数
- 如果 t m p b < 0 tmpb < 0 tmpb<0 说明第二组无法凑齐t人,说明不可行
- 如果剩余b中的人数大于 n / 2 − t n/2 - t n/2−t 说明,还有一部分b仍然需要填充到第二组,使第二组中会杂耍的大于 t t t个,说明不可行
- 以及剩余 c c c中的人数大于 n / 2 − t n/2 - t n/2−t ,说明不可行(同理与10)
- 最后的情况即为可行情况,记录第一组中每种人数需要选的人数,循环遍历即可
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)上询问,将此位置的行列的数值离散化,使其在列中不改变相对大小,行中不改变相对大小,使该行列的最大值最小,并输出
解题思路:
- 分别按照行和列将其离散化,并存到两个不同的数组 r [ ] [ ] r[][] r[][], c [ ] [ ] c[][] c[][],以及记录行 M a x r [ ] Maxr[] Maxr[]和列 M a x c [ ] Maxc[] Maxc[]离散化后最大的值
- 将每个位置的 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]比较输出大的那个
- 其余情况同理
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中出现的最多
解题思路:
- 利用 k m p kmp kmp中求 n e x t next next数组的方法,求出长度为 t . s i z e ( ) t.size() t.size()的最长相同前缀后缀长度
- 然后每次排到 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;
}