CDZSC_2022寒假个人训练赛21级(8)题解
- 简单
- F 简单dp、贪心
- G 字符串
- H
- 中等
- A 模拟
- C 并查集
- D 最小生成树
- E 模拟
- 困难
- B bfs
A Extract Numbers CodeForces - 600A
题意
给定一个字符串,字符';' 和 ','将串分开,每部分是一个单词,若出现中间为空的情况(连续两个分隔符),则认为中间的单词是空格。若单词是整数(不能有前导0),则放在串a里面,反之放在串b里面。在a,b串中,单词用','分开。若无单词是整数,a串为"-",若无单词是非整数,b串为"-"。最后输出a串和b串,如a串(b串)不是"-",输出时用""括起。
题解
模拟
AC代码
bool check(string s) {
if (s == "") return 0;
if (s[0] == '0' && s.size() > 1) return 0;
for (int i = 0; i < s.size(); i++) {
if (s[i] >= '0' && s[i] <= '9') continue;
return 0;
}
return 1;
}
int main () {
string s; cin >> s;
s += ",";
int now = 0;
string a = "\"", b = "\"";
while (now < s.size()) {
string t;
while (now < s.size() && s[now] != ',' && s[now] != ';') t.push_back(s[now]), now++;
now++;
if (check(t)) {
a += t + ",";
} else {
b += t + ",";
}
}
a.pop_back(), b.pop_back();
a += "\"", b += "\"";
if (a == "\"") a = "-";
if (b == "\"") b = "-";
cout << a << endl << b << endl;
return 0;
}
B Kilani and the Game CodeForces - 1105D
题意
一个棋盘,\(p\)种颜色已经涂了一些区域,每种颜色有扩散速度 \(s_i\) ,每轮按顺序依次扩展,扩展区域为已有区域任意格走 \(s_i\) 次能到达的可涂色格子,且不能穿过其他颜色与不可涂色格,棋盘无可涂色区域游戏结束,输出各种颜色格子数。
题解
两个bfs模拟就好,除了麻烦点,难度其实挺低,如果bfs已经学了,但代码写不出来,属于基本功不行。
这题cf 分数高纯属于cf时间太短,上面的A题也一样,在acm赛制下 cf 对长代码的题分数虚高。
AC代码
#include<iostream>
#include<algorithm>
#include<set>
#include<stdio.h>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
struct node{
int x, y, step;
node(int a,int b,int c):x(a),y(b),step(c){}
};
int n, m,p;
int s[10];
int dx[] = { 0,0,1,-1 };
int dy[] = { 1,-1,0,0 };
char plat[1005][1005];
queue<node> pq[10],q;
int ans[10];
void bfs(int num) {
while (q.size()) {
node tmp = q.front();
q.pop();
if (tmp.step == 0) {
tmp.step = s[num];
pq[num].push(tmp);
continue;
}
for (int i = 0; i < 4; i++) {
int x = tmp.x + dx[i];
int y = tmp.y + dy[i];
if (x < 0 || x >= n || y < 0 || y >= m || plat[x][y] != '.')continue;
plat[x][y] = num+'0';
q.push(node(x, y, tmp.step - 1));
}
}
}
bool act(int x) {
while (pq[x].size()) {
q.push(pq[x].front());
pq[x].pop();
}
bfs(x);
return pq[x].size();
}
int main() {
scanf("%d%d%d", &n, &m, &p);
for (int i = 1; i <= p; i++)
scanf("%d", s + i);
for (int i = 0; i < n; i++)
scanf("%s", plat + i);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if ('1'<=plat[i][j]&& plat[i][j] <='9') {
pq[plat[i][j] - '0'].push(node(i, j, s[plat[i][j] - '0']));
}
}
}
while (1) {
int flag = 1;
for (int i = 1; i <= p; i++) {
if (act(i))flag = 0;
}
if (flag)break;
}
for(int i=0;i<n;i++)
for (int j = 0; j < m; j++) {
if ('1' <= plat[i][j]&&plat[i][j] <= '9') {
ans[plat[i][j] - '0']++;
}
}
for (int i = 1; i <= p; i++)
printf("%d ", ans[i]);
return 0;
}
C Wireless Network POJ - 2236
题意
每台电脑只能与距离它不超过 d 米的其它电脑直接通信。但每台电脑可被看作其它两台电脑的通信中转点,也就是说,如果电脑 A 和电脑 B 可以直接通信,或存在一台电脑 C 既可与 A 也可与 B 通信,那么电脑 A 和电脑 B 之间就能够通信。
在处理网络修复的过程中,工作人员们在任何一个时刻,可以执行两种操作:维修一台电脑,或测试两台电脑是否能够通信。请您找出全部的测试操作。
题解
并查集,维修操作的时候,将维修的节点和所有可连节点连接,查询就直接查询是否在同一个集合。
set 启发式合并也可以做,反正只要能快速维护集合的合并,并且快速查找是否存在对应元素的数据结构都可以做。只不过并查集比较好写,也简单。
AC代码
#include<iostream>
#include<math.h>
using namespace std;
int s[1005][2];
int l[1005];
int h[1005];
int d;
void init(int n) {
for (int i = 1; i <= n; i++)
l[i] = i;
}
int find(int x) {
return x == l[x] ? x : find(l[x]);
}
void merge(int a,int b) {
a = find(a);
b = find(b);
l[a] = b;
}
bool equal(int a, int b) {
return find(a) == find(b);
}
bool near(int a, int b) {
double dx = s[a][0] - s[b][0];
double dy = s[a][1] - s[b][1];
double dis = sqrt(dx*dx + dy * dy);
return dis <= d;
}
int main() {
int n, k=0;
scanf("%d%d", &n, &d);
init(n);
for (int i = 1; i <= n; i++) {
scanf("%d%d", s[i],s[i]+1);
}
char c;
while (~scanf("%c", &c)) {
if (c == 'O') {
int x;
int flag = 1;
scanf("%d", &x);
for (int i = 0; i < k; i++) {
if (near(x, h[i])) {
merge(x, h[i]);
}
}
h[k++] = x;
}
else if (c == 'S') {
int a, b;
scanf("%d%d", &a, &b);
if (equal(a, b))
printf("SUCCESS\n");
else printf("FAIL\n");
}
}
}
D Jungle Roads POJ - 1251
题意
有一个旅游区,旅游区有很多的景点,景点间需要开通缆车,使得任意两个景点可以互相到达。现在给出一些点间的缆车线路制造成本,两个景点之间可能有多重制造方式。问最少的花费是多少。
题解
最小生成树裸题。
AC代码
#include<iostream>
#include<algorithm>
using namespace std;
struct edge {
int u, v, w;
bool operator<(edge x) {
return w < x.w;
}
};
int s[100];
int h[100];
edge e[100];
void init(int n) {
for (int i = 0; i < n; i++) {
s[i] = i;
h[i] = 0;
}
}
int find(int x) {
if (x != s[x])s[x] = find(s[x]);
return s[x];
}
void near(int a, int b) {
a = find(a), b = find(b);
if (h[a] == h[b]) {
s[b] = a;
h[a] = h[a] + 1;
}
else {
if (h[a] < h[b])s[a] = b;
else s[b] = a;
}
}
int main() {
int n;
while (scanf("%d", &n), n) {
init(n);
int k = 0,ans=0;
for (int i = 0; i < n - 1; i++) {
char a, b;
int m,x;
cin >> a >> m;
while (m--) {
cin >> b >> x;
e[k].u = a-'A';
e[k].v = b-'A';
e[k++].w = x;
}
}
sort(e, e + k);
for (int i = 0; i < k; i++) {
int a = find(e[i].u), b = find(e[i].v);
if (a == b)continue;
s[b] = a;
ans += e[i].w;
}
printf("%d\n", ans);
}
return 0;
}
E s-palindrome CodeForces - 691B
题意
判断字符串是否左右镜像对称
题解
AHIMOoTUVvWwXxY 这些字符本身是对称的,由这些字符构成的子串都能左右镜像对称。
bd、 pq ,这些字符成对对称,左右两边如果出现对应的也能对称
AC代码
char s[N];
char mirror[]="AHIMOoTUVvWwXxY";
char sym[]="bdpq";
bool isSym(char a,char b){
for(int i=0;s[i];i++){
if(!s[i+1])continue;
if(sym[i]==a&&sym[i+1]==b)
return 1;
if(sym[i+1]==a&&sym[i]==b)
return 1;
}
return 0;
}
bool in(char *s,char c){
for(int i=0;s[i];i++)
if(s[i]==c)return 1;
return 0;
}
int main(){
scanf("%s",s);
int flag=1,n=0;
for(;s[n];n++){
if(!in(mirror,s[n])&&!in(sym,s[n])){
flag=0;
break;
}
}
for(int i=0;i<n/2;i++){
if(!(isSym(s[i],s[n-1-i])||(in(mirror,s[i])&&s[i]==s[n-1-i])))
flag=0;
if(flag==0)break;
}
if(flag&&n&1)flag=in(mirror,s[n/2]);
printf("%s\n",flag?"TAK":"NIE");
scanf(" ");
}
F The Way to Home CodeForces - 910A
题意
一只青蛙现在在一个数轴上,它现在要从点 1 跳到点 \(n\) ,它每次可以向右跳不超过 \(d\) 个单位。比如,它可以从点 \(x\) 跳到点 \(x+a(1≤a≤d)\) 。
题解
简单dp或者贪心,或者dfs,反正数据就100,随便做都行。
AC代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
int dp[105];
char s[105];
int main() {
int n, m;
memset(dp, 0x3f, sizeof dp);
scanf("%d%d%s", &n, &m, s + 1);
dp[1] = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m && j + i <= n; j++)
if (s[i + j] == '1')
dp[i + j] = min(dp[i + j], dp[i] + 1);
printf("%d\n", dp[n] == 0x3f3f3f3f ? -1 : dp[n]);
return 0;
}
G Generate Login CodeForces - 909A
题意
给定两个用空格分隔的字符串,分别取两字符串的任意非空前缀,将两前缀合并为一个新的字符串,求可行字典序最小的字符串。
题解
因为字典序最小所以尽量要短,先输出必须要有的\(a[0]\),然后根据字典序号大小来判断是直接输出\(b[0]\)然后结束还是继续输出\(a\)的后续字符
AC代码
#include<iostream>
#include<cmath>
using namespace std;
char a[20],b[20];
int main(){
int n,r;
scanf("%s%s",a,b);
printf("%c",a[0]);
for(int i=1;a[i];i++){
if(a[i]>=b[0]){
printf("%c",b[0]);
return 0;
}
printf("%c",a[i]);
}
printf("%c",b[0]);
return 0;
}
H Tricky Alchemy CodeForces - 912A
题意
要生产一个黄色球,需要两颗黄色晶体,生产绿色需要一黄一蓝,生产蓝色,需要三蓝。
给出黄色晶体和蓝色的数量,和要生产的球,问还需要多少晶体。
题解
AC代码
int main() {
ll a, b, x, y, z,aa,bb;
scanf("%lld%lld%lld%lld%lld", &a, &b, &x, &y, &z);
aa = x * 2 + y;
bb = y + z * 3;
ll ans = 0;
if (aa - a > 0)ans += aa - a;
if (bb - b > 0)ans += bb - b;
printf("%lld\n", ans);
return 0;
}