题目描述
小黑家所在的小区最近很不幸地被划分为了中风险地区,为了响应政府疫情防控的号召,小黑全家居家隔离,等待疫情稳定下来。但隔离的日子总是异常漫长与难熬,小黑为了让他的儿子小黑子多动动脑筋,而不是整天在家无所事事、好吃懒做,小黑决定出一道有关于字符串的题目来考考小黑子,具体问题如下:
1、构造一个字符串仅由 0/1 组成 ,其长度为 n 并且其中含 a 个 1 ,b 个 0
2、满足 si≠si−1(1<i≤n) 的下标共有 x 个
3、满足条件的字符串输出任意一种即可
对于这道题,小黑子很快的说出了答案,所以小黑决定加大难度,让小黑子告诉他这些满足条件的字符串中字典序最大的字符串是哪一个?这可就把小黑子难倒了,所以现在他决定来向你求助,请你来帮助他解决这道字符串的问题。即找出所有符合要求的字符串中字典序最大的字符串,若没有符合要求的字符串则输出 −1
字典序比较:对于两个长度为 n 字符串s1: s11s12⋅⋅⋅⋅s1n 和s2: s21s22⋅⋅⋅⋅s2n ,字典序 s1>s2 :当且仅当存在下标 i ,使得s1i>s2i, 并且 s11s12⋅⋅⋅⋅s1i−1 和 s21s22⋅⋅⋅⋅s2i−1 是完全相同的
输入描述
第一行一个正整数 T (T≤500),代表 T 组样例
接下来的 T 行,每行三个整数 a、b、x ,其中 0≤a、b≤500,0≤x≤1000
输入保证 a、b 不同时为 0
输出描述
每组样例输出一个字典序最大并且符合要求的字符串,没有符合要求的字符串则输出 −1 。
行尾无空格
样例
输入
4 2 3 4 2 2 2 4 3 3 4 2 5输出
01010 1001 1110100 -1
解题思路
要得到字典序最大的排序方式,1要尽可能靠左,0要尽可能靠右
而得到x个下标满足条件的串长度至少为x+1且必定是01交替排序
而在这个长度为x+1的子串里,拆分为最少的0和1的数量为:(x+1)/2 和 (x+1)-(x+1)/2
这里可能有人疑问,为什么得到最少足够满足条件的串长一定是x+1且一定01交替?
基于这样一个事实:
如果该串不是01交替,那么我们所需要的是最大字典序的串,里面完全可以把连续的1中多余的(不起效果:Xi == Xi+1)的1提到字符串最左边,而连续的0,可以把多余的提到字符串最右边
使得所有字符效果均最大化
所以我们问题可以简化为:用已有的0和1,在代价最小,收益最大的情况下先满足长度为x+1的条件字符串,再把多余的1放到左边,多余的0放到右边即可
对于构建这个条件字符串,我们只要遵循以下规则:
因为现有0和1如果数量足够,这个串中一定可以拆分为上述提到的两个数量need1,need2
且根据int类型的计算,need2>=need1(当need2>need1时,必有need2 == need1 + 1)
①如果已有的1的数量足有need2,0的数量足有need1,那么这个串按1010101....构建
②如果已有的1的数量足有need1,0的数量足有need2,那么这个串按0101010....构建
如果0或者1有一个的数量不足need1,那么则无法构建这个条件串
条件②优先级一定要比条件①低,因为在1够的情况下一定是同时大于need1和need2,而以1开头能让条件串的字典序尽可能大(这个也是决定最终结果字典序大小的关键部分)
思考①:是不是可以无脑构建条件串,左边添1右边添0呢?
答案:显然不行
如果条件串以1结尾,在条件串右边添0,那么会导致我们结果比需要的x多1个
这时候我们只需要判断条件串是否是以1结尾,如果是则条件串不输出最后的1,而在补完所有的0以后,在整个字符串的最后添上那个1(这里补的0都是无效0,即不会影响有多少个满足条件的下标)
思考②:那无脑左边添1是否会影响结果呢?
答案:不会
因为既然左边能添1,证明条件串中的1是充足的,即1的数量大于等于need1和need2,那么我们一定是按101010...的形式构建条件串,那么左边添1都是属于无效1,不会影响结果
核心代码
void printc(char c, int n) {
//以c字符开始,输出长度为n+1的条件串
for (int i = 0; i <= n; i++) {
printf("%c", c);
c = c == '1' ? '0' : '1';
}
}
int need1 = (x + 1) / 2;
int need2 = (x + 1) - need1;
//如果x为0,只要ab不都为0则不存在
if((!x)&&a&&b){printf("-1\n");continue;}
//情况1
if (a >= need2 && b >= need1) {
print('1', a - need2);
if (need2 == need1) { //如果need1==need2则是以0结尾的,形如10101010
//字符串的构成为(11111....+条件串+0000...)
printc('1', x);
print('0', b - need1);
} else { //条件串是以1结尾的,形如1010101
//这种情况的字符串构成为(1111....+条件串[去掉末尾]+0000...)
printc('1', x - 1); //条件串少输出一个
print('0', b - need1);
printf("1"); //在整个字符串的最后面输出条件串末尾的1
}
printf("\n");
//情况2
} else if (a == need1 && b >= need2) {
//这种情况不存在前缀1,整个字符串的构成为(条件串+00000)
printc('0', x);
print('0', b - need2);
printf("\n");
} else printf("-1\n");
完整代码
#include<iostream>
void print(char c, int n) {
for (int i = 0; i < n; i++)printf("%c", c);
}
void printc(char c, int n) {
for (int i = 0; i <= n; i++) {
printf("%c", c);
c = c == '1' ? '0' : '1';
}
}
int main() {
int n;
scanf("%d", &n);
while (n--) {
int a, b, x;
scanf("%d%d%d", &a, &b, &x);
int need1 = (x + 1) / 2;
int need2 = (x + 1) - need1;
if((!x)&&a&&b){printf("-1\n");continue;}
if (a >= need2 && b >= need1) {
print('1', a - need2);
if (need2 == need1) {
printc('1', x);
print('0', b - need1);
} else {
printc('1', x - 1);
print('0', b - need1);
printf("1");
}
printf("\n");
} else if (a == need1 && b >= need2) {
printc('0', x);
print('0', b - need2);
printf("\n");
} else printf("-1\n");
}
}