Codeforces Round #644 (Div. 3) A-H 题解

题目链接: https://codeforces.com/contest/1360

A. Minimal Square

题目大题: 将长宽为a, b的两个相同矩形嵌入正方形中, 问正方形的最小面积.
解题思路: 找到长宽, 并判断两倍的宽是否大于长.

/*
 * @Author: Hellcat
 * @Date: 2020-05-24 23:54:16
 */
#include <bits/stdc++.h>
using namespace std;
 
int main() {
    int T; cin>>T;
    while(T--) {
        int a, b; cin>>a>>b;
        int Min = min(a, b);
        int Max = max(a, b);
        if(2*Min >= Max)
            cout<<pow(2*Min, 2)<<endl;
        else
            cout<<pow(Max, 2)<<endl;
    }
}

B. Honest Coach

题目大意: 将一个数组分为A, B两组, 使得|max(A)−min(B)|最小.
解题思路: 将数组小到大排序, 找到相邻两个元素最小的差值即为所求.

/*
 * @Author: Hellcat
 * @Date: 2020-05-24 23:54:16
 */
#include <bits/stdc++.h>
using namespace std;
int a[55];
 
int main() {
    int T; cin>>T;
    while(T--) {
        int n; cin>>n;
        for(int i = 0; i < n; i++) cin>>a[i];
        sort(a, a + n);
        int Min = 0x3f3f3f3f;
        for(int i = 0; i < n-1; i++) {
            Min = min(Min, a[i+1] - a[i]);
        }
        cout<<Min<<endl;
    }
}

C. Similar Pairs

题目大意: 定义奇偶性相同的一对数或满足差值为1的一对数(|x−y|=1)为Similar Pairs. 输入的数组元素个数为偶数.
解题思路: 若输入的奇偶个数均为偶数, 则满足配对关系. 否则, 对数组排序后查找是否有满足差值为1的相邻元素. 如果有则满足题意, 否则不满足.

/*
 * @Author: Hellcat
 * @Date: 2020-05-24 23:54:16
 */
#include <bits/stdc++.h>
using namespace std;
int a[55];
 
int main() {
    int T; cin>>T;
    while(T--) {
        int n; cin>>n;
        int e = 0, d = 0;
        for(int i = 0; i < n; i++) {
            cin>>a[i];
            if(a[i] & 1) d++;
            else e++;
        }
        if((e&1) == 0) { puts("YES"); continue; }
        sort(a, a+n);
        int diff = 0;
        for(int i = 0; i < n - 1; i++) {
            diff = a[i+1] - a[i];
            if(diff == 1) break;
        }
        if(diff == 1) { puts("YES"); continue; }
        puts("NO");
    }
}

D. Buying Shovels

题目大意: 买n个铁锹, 只能从给定的1~k种包装中选择一种, 全部购买这种包装的商品. 求最小购买的商品数量.
解题思路: 首先想到的是暴力枚举, 而1e9的数据量必然TLE, 随后针对暴力解法进行优化. 对特殊情况单独判定后, 结合素数判定的思路, 只要考虑前sqrt(n)的情况. 枚举2~sqrt(n)之间的数, 如果能被整除, 且满足i <= k, 则更新答案. 更新时考虑2种情况, 1)是选择包含i件商品的包裹, 购买n/i件. 2)是满足n/i <= k时选择包含n/i件商品的包裹, 购买i件. 这样一来, 时间复杂度可优化到O(logn)级别.

/*
 * @Author: Hellcat
 * @Date: 2020-05-24 23:54:16
 * https://codeforces.com/contest/1360/problem/D
 * D. Buying Shovels
 */
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int main() {
    int T; cin>>T;
    while(T--) {
        int n, k; cin>>n>>k;
        if(k >= n) { puts("1"); continue; }
        if(k == 1) { cout<<n<<endl; continue; }
        int m = floor(sqrt(n) + 0.5);
        int res = n;
        for(int i = 2; i <= m; i++) {
            if(n%i == 0 && i <= k) { res = min(res, n/i); if(n/i <= k) res = min(res, i); }
        }
        cout<<res<<endl;
    }
}

E. Polygon

题目大意: 训练发射炮弹, 每次只能从左侧发射到右侧或上方发射到下方, 碰到1或边界为止, 然后将所在位置由0改为1. 满足这样要求的训练输出"YES". 如图所示:

解题思路: 首先想到dfs中的连通图思路, 从右侧及下方的1作为起点遍历. 最后如果没有剩下1则满足题意, 否则不满足. 而这题只有2个方向, 这样做显然有些复杂. 考虑不满足题意的情况, 必然存在一个1其左侧以及下方均为0. 遍历全图判断是否有这样的1即可. 同时注意, 在双重循环内一次break并不能完全跳出, 需要在双重循环外进行输出, 否则会出现输出多个"NO"的情况.

/*
 * @Author: Hellcat
 * @Date: 2020-05-25 11:44:21
 */
#include <bits/stdc++.h>
using namespace std;
char maze[55][55];
 
int main() {
    int T; cin>>T;
    while(T--) {
        int n; cin>>n;
        for(int i = 1; i <= n; i++) cin>>maze[i]+1;
 
        bool flag = 1;
        for(int i = 1; i < n; i++)
            for(int j = 1; j < n; j++)
                if(maze[i][j] == '1' && maze[i+1][j] == '0' && maze[i][j+1] == '0')
                    { flag = 0; break; }
        if(flag) puts("YES");
        else puts("NO");
    }
}

F. Spy-string

题目大意: 给出n个字符串a1, a2, …, an, 如果字符串s对每个字符串都满足不相同字符不超过一个(ai[j] ≠ s[j]的个数 < 1), 输出这样的字符串s.
解题思路: 题给数据量不大, 输入字符串个数n(1≤n≤10)和每个字符串的长度m(1≤m≤10). 字符串均由英文字符组成, 考虑暴力的解法. 由于不相同字符不超过1个, 那么将输入的第一个字符串的每个字符逐个改为'a'~'z', 再对第2~n个字符串遍历, 检查是否满足不相同字符不超过一个. 满足则输出s, 不满足输出-1.

/*
 * @Author: Hellcat
 * @Date: 2020-05-25 14:47:14
 */
#include <bits/stdc++.h>
using namespace std;
 
string s[11], res;
int n, m;
 
bool check() {
    for(int i = 2; i <= n; i++) {
        int cnt = 0;
        for(int j = 0; j < m; j++) {
            if(s[i][j] != res[j])
                cnt++;
            if(cnt > 1) return 0;
        }
    }
    return 1;
}
 
void slove() {
    for(int i = 0; i < m; i++) {
        for(char ch = 'a'; ch <= 'z'; ch++) {
            res[i] = ch;
            if(check()) {
                cout<<res<<endl;
                return;
            }
            res[i] = s[1][i]; // 再改回去
        }
    }
    puts("-1");
}
 
int main() {
    int T; cin>>T;
    while(T--) {
        cin>>n>>m;
        for(int i = 1; i <= n; i++) cin>>s[i];
        res = s[1];
 
        slove();
    }
}

G. A/B Matrix

题目大意: 给定一个\(n×m\)的矩阵, 使得每行有a个1, 每列有b个1, 其余元素均为0.
例如n=3, m=6, a=2, b=1时, 矩阵为

\[\left[ \begin{matrix} 0 & 1 & 0 & 0 & 0 & 1\\ 1 & 0 & 0 & 1 & 0 & 0\\ 0 & 0 & 1 & 0 & 1 & 0\\ \end{matrix} \right] \]

解题思路: 先考虑不满足题设的情况. 对于行, 矩阵中1的个数为\(a \times n\)个, 对于列则应为\(b \times m\)个, 当\(a \times n != b \times n\)时输出NO.
其次, 将矩阵初始化为0, 考虑矩阵中如何填充1. 将题目中的样例转化为更一般的矩阵, 如:

\[\left[ \begin{matrix} 1 & 1 & 0 & 0 & 0 & 0\\ 0 & 0 & 1 & 1 & 0 & 0\\ 0 & 0 & 0 & 0 & 1 & 1\\ \end{matrix} \right] \]

也是满足题设条件的输出. 对于第四个样例4 4 2 2输出的矩阵, 也可以转为

\[\left[ \begin{matrix} 1 & 1 & 0 & 0 \\ 0 & 0 & 1 & 1 \\ 1 & 1 & 0 & 0 \\ 0 & 0 & 1 & 1 \\ \end{matrix} \right] \]

多次尝试, 得到一般的规律后, 有如下思路: 单独考虑行, 在每行填充a个1, 用col维护填充的字符\(1\)的纵坐标, 根据规律只需要每次填充时++col. 同时为防止越界将col %= m即可.

/*
 * @Author: Hellcat
 * @Date: 2020-05-26 08:55:48
 */
#include <bits/stdc++.h>
using namespace std;
 
bool res[55][55];
 
int main() {
    int T; cin>>T;
    while(T--) {
        memset(res, 0, sizeof res);
        int n, m, a, b; cin>>n>>m>>a>>b;
        if(a*n != b*m) { puts("NO"); continue; }
        int col = 0;  // column position
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= a; j++)
                res[i][++col] = 1, col %= m;
 
        puts("YES");
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= m; j++)
                cout<<res[i][j];
            puts("");
        }
    }
}

H. Binary Median

题目大意: 给定二进制字符串长度\(m\)(\(1\le m \le60\)), 按字典序生成总共\(2^m\)个长度为\(m\)的二进制字符串. 然后从中删去n个字符串, 剩下\(k=2^m-n\)个字符串. 按照字典序排列后输出第\(\lfloor \frac{k-1}{2} \rfloor\)个字符串. 例如, 当\(n=3\), \(m=3\)\(a=[010, 111, 001]\)时, 从所有长度为3的二进制字符串中移去\(a_i\), 得到集合\([000, 011, 100, 101, 110]\), 输出中位数100.
解题思路: 数据均为二进制数, 很容易想到使用bitset库. 集合的大小为\(2^m\), 若从中删除元素时间复杂度过高. 考虑使用光标移动替代集合删除元素的过程. 因此需要先求出中间位置\(mid=\lfloor \frac{k-1}{2} \rfloor\), 如果要删除的元素\(\le\)\(mid\), 那么将\(mid\)++, 最后得到的\(mid\)即中间元素十进制值, 用to_string函数转为字符串输出, 并用substr选取最右m位即可.
由于需要对输入元素排序, 所以要用到std::bitset::to_ullong函数, 该函数的作用是将二进制数转为10进制的unsigned long long. 如图所示: 这样一来便可以用sort进行排序.

/*
 * @Author: Hellcat
 * @Date: 2020-05-26 11:29:43
 */
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int main() {
    int T; cin>>T;
    while(T--) {
        int n, m; cin>>n>>m;
        ll a[n+10] = {0};
        ll mid = (1ll<<m)-n-1>>1;
        for(int i = 1; i <= n; i++) {
            string s; cin>>s;
            a[i] = bitset<64>(s).to_ullong();
        }
        sort(a+1, a+1+n);
        for(int i = 1; i <= n; i++)
            if(a[i] <= mid) mid++;
        cout<<bitset<64>(mid).to_string().substr(64-m)<<endl;
    }
}
posted @ 2020-05-25 23:55  hellcat9  阅读(187)  评论(0编辑  收藏  举报